40 Commits

Author SHA1 Message Date
163e1f73d4 fix: update D1 schema to match v0.4.0 service-to-offers relationship
- Add service_id column to offers table
- Remove offer_id column from services table
- Add index for service_id in offers
2025-12-07 22:31:34 +01:00
1d47d47ef7 feat: add database migration for service-to-offers refactor
- Add service_id column to offers table
- Remove offer_id column from services table
- Update VERSION to 0.4.0 in wrangler.toml
2025-12-07 22:28:14 +01:00
1d70cd79e8 feat: refactor to service-based WebRTC signaling endpoints
BREAKING CHANGE: Replace offer-based endpoints with service-based signaling

- Add POST /services/:uuid/answer
- Add GET /services/:uuid/answer
- Add POST /services/:uuid/ice-candidates
- Add GET /services/:uuid/ice-candidates
- Remove all /offers/* endpoints (POST /offers, GET /offers/mine, etc.)
- Server auto-detects peer's offer when offerId is omitted
- Update README with new service-based API documentation
- Bump version to 0.4.0

This change simplifies the API by focusing on services rather than individual offers.
WebRTC signaling (answer/ICE) now operates at the service level, with automatic
offer detection when needed.
2025-12-07 22:17:24 +01:00
2aa1fee4d6 docs: update server README to remove outdated sections
- Remove obsolete POST /index/:username/query endpoint
- Remove non-existent PUT /offers/:offerId/heartbeat endpoint
- Update architecture diagram to reflect semver discovery
- Update database schema to show service-to-offers relationship
2025-12-07 22:07:16 +01:00
d564e2250f docs: Update README with semver matching and offers array 2025-12-07 22:00:40 +01:00
06ec5020f7 0.3.0 2025-12-07 21:59:15 +01:00
5c71f66a26 feat: Add semver-compatible service discovery with privacy
## Breaking Changes

### Removed Endpoints
- Removed GET /users/:username/services (service listing)
- Services are now completely hidden - cannot be enumerated

### Updated Endpoints
- GET /users/:username/services/:fqn now supports semver matching
- Requesting chat@1.0.0 will match chat@1.2.3, chat@1.5.0, etc.
- Will NOT match chat@2.0.0 (different major version)

## New Features

### Semantic Versioning Support
- Compatible version matching following semver rules (^1.0.0)
- Major version must match exactly
- For major version 0, minor must also match (0.x.y is unstable)
- Available version must be >= requested version
- Prerelease versions require exact match

### Privacy Improvements
- All services are now hidden by default
- No way to enumerate or list services for a username
- Must know exact service name to discover

## Implementation

### Server (src/)
- crypto.ts: Added parseVersion(), isVersionCompatible(), parseServiceFqn()
- storage/types.ts: Added findServicesByName() interface method
- storage/sqlite.ts: Implemented findServicesByName() with LIKE query
- storage/d1.ts: Implemented findServicesByName() with LIKE query
- app.ts: Updated GET /:username/services/:fqn with semver matching

### Semver Matching Logic
- Parse requested version: chat@1.0.0 → {name: "chat", version: "1.0.0"}
- Find all services with matching name: chat@*
- Filter to compatible versions using semver rules
- Return first match (most recently created)

## Examples

Request: chat@1.0.0
Matches: chat@1.0.0, chat@1.2.3, chat@1.9.5
Does NOT match: chat@0.9.0, chat@2.0.0, chat@1.0.0-beta

🤖 Generated with Claude Code
2025-12-07 21:56:19 +01:00
ca3db47009 Refactor: Consolidate service/offer architecture
## Breaking Changes

### Server
- Services can now have multiple offers instead of single offer
- POST /users/:username/services accepts `offers` array instead of `sdp`
- GET /users/:username/services/:fqn returns `offers` array in response
- GET /services/:uuid returns `offers` array in response
- Database schema: removed `offer_id` from services table, added `service_id` to offers table
- Added `batchCreateServices()` and `getOffersForService()` methods

### Client
- `PublishServiceOptions` interface: `offers` array instead of `sdp` string
- `Service` interface: `offers` array instead of `offerId` and `sdp`
- `ServiceRequest` interface: `offers` array instead of `sdp`
- RondevuSignaler.setOffer() sends offers array to server
- Updated to extract offerId from first offer in service response

## New Features
- Support for multiple simultaneous offers per service (connection pooling)
- Batch service creation endpoint for reduced server load
- Proper one-to-many relationship between services and offers

## Implementation Details

### Server Changes (src/storage/)
- sqlite.ts: Added service_id column to offers, removed offer_id from services
- d1.ts: Updated to match new interface
- types.ts: Updated interfaces for Service, Offer, CreateServiceRequest
- app.ts: Updated all service endpoints to handle offers array

### Client Changes (src/)
- api.ts: Added OfferRequest and ServiceOffer interfaces
- rondevu-service.ts: Updated PublishServiceOptions to use offers array
- rondevu-signaler.ts: Updated to send/receive offers array

## Migration Notes
- No backwards compatibility - this is a breaking change
- Services published with old API will not work with new server
- Clients must update to new API to work with updated server

🤖 Generated with Claude Code
2025-12-07 21:49:23 +01:00
3efed6e9d2 Fix service reconnection: return available offer from pool
Modified /services/:uuid endpoint to return an available (unanswered)
offer from the service's offer pool instead of always returning the
initial offer. This fixes reconnection failures where clients would
try to answer already-consumed offers.

Changes:
- Query all offers from the service's peer ID
- Return first unanswered offer
- Return 503 if no offers available

Fixes: "Offer already answered" errors on reconnection attempts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-06 13:47:00 +01:00
1257867dff fix: implement upsert behavior for service creation
When a service is republished (e.g., for TTL refresh), the old service
is now deleted before creating a new one, preventing UNIQUE constraint
errors on (username, service_fqn).

Changes:
- Query for existing service before creation
- Delete existing service if found
- Create new service with same username/serviceFqn

This enables the client's TTL auto-refresh feature to work correctly.
2025-12-06 13:04:45 +01:00
52cf734858 Remove legacy V1 code and clean up unused remnants
- Delete unused bloom.ts module (leftover from topic-based discovery)
- Remove maxTopicsPerOffer configuration (no longer used)
- Remove unused info field from Offer types
- Simplify generateOfferHash() to only hash SDP (remove topics param)
- Update outdated comments referencing deprecated features
- Remove backward compatibility topics field from answer responses

This completes the migration to V2 service-based architecture by
removing all remnants of the V1 topic-based system.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-06 12:06:02 +01:00
5622867411 Add upsert behavior to service creation
- Delete existing service before creating new one
- Prevents UNIQUE constraint error on (username, service_fqn)
- Enables seamless service republishing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-06 11:46:21 +01:00
ac0e064e34 Fix answer response field names for V2 API compatibility
- Change 'answererPeerId' to 'answererId'
- Change 'answerSdp' to 'sdp'
- Add 'topics' field (empty array) for client compatibility

This ensures the server response matches the expected format
in the client's AnsweredOffer interface.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-06 11:37:31 +01:00
e7cd90b905 Fix error handling scope issue in service creation
The error handler was referencing variables (username, serviceFqn, offers)
that were declared inside the try block. If an error occurred before these
were defined, the error handler itself would fail, resulting in non-JSON
responses that caused "JSON.parse: unexpected character" errors on the client.

Fixed by:
- Declaring variables at function scope
- Initializing offers as empty array
- Using destructuring assignment for username/serviceFqn

This ensures the error handler can always access these variables safely,
even if an early error occurs, and will always return proper JSON responses.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:56:06 +01:00
67b1decbad debug: add detailed error logging to service creation endpoint
Return error details in response to help debug internal server errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:37:57 +01:00
e9d0f26726 fix: add validateServicePublish for correct signature verification
The service publishing endpoint was using validateUsernameClaim which
expects the message format "claim:{username}:{timestamp}", but clients
send "publish:{username}:{serviceFqn}:{timestamp}".

Added validateServicePublish function to properly validate service
publishing signatures with the correct message format.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:31:42 +01:00
595eac8692 feat: add V2 database migration for D1
Add migration to create V2 tables:
- offers (with ICE candidates)
- usernames (with Ed25519 public keys)
- services (with service discovery)
- service_index (privacy layer)

Applied to production D1 database: rondevu-offers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:24:29 +01:00
65a13fefa4 fix: use async ed25519.verifyAsync function
Switch from sync verify() to async verifyAsync() to work with
hashes.sha512Async which uses WebCrypto API.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:19:47 +01:00
1dadf5461e fix: use Web Crypto API for Cloudflare Workers compatibility
- d1.ts: Use global crypto.randomUUID() instead of importing from 'crypto'
- sqlite.ts: Use 'node:crypto' import for Node.js compatibility

This fixes the Cloudflare Workers deployment error:
"The package 'crypto' wasn't found on the file system but is built into node"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:05:23 +01:00
bd35f7919c chore: bump version to 0.2.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 18:46:00 +01:00
683bc42bf0 fix: initialize SHA-512 hash function for @noble/ed25519 v3
@noble/ed25519 v3.0.0 requires explicit SHA-512 hash function setup
before using any cryptographic operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 18:45:04 +01:00
c3fc498c81 fix: correct server version to 0.2.0 (minor bump from 0.1.4)
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 18:30:29 +01:00
4f772c50c9 feat: add V2 service publishing and username claiming APIs
- Add POST /services endpoint for publishing services with username verification
- Add DELETE /services/:serviceId endpoint for unpublishing services
- Add GET /services/:serviceFqn endpoint for service discovery
- Add POST /usernames/claim endpoint with Ed25519 signature verification
- Add POST /usernames/renew endpoint for extending username TTL
- Add GET /usernames/:username endpoint for checking username availability
- Add username expiry tracking and cleanup (365-day default TTL)
- Add service-to-offer relationship tracking
- Add signature verification for username operations
- Update storage schema for usernames and services tables
- Add comprehensive README documentation for V2 APIs
- Update version to 0.8.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 18:27:12 +01:00
08e1433088 Update README: Remove custom peer ID documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 23:23:09 +01:00
70d018c666 Remove custom peer ID feature for security
Always generate cryptographically random 128-bit peer IDs to prevent peer ID hijacking vulnerability. This ensures peer IDs are secure through collision resistance rather than relying on expiration-based protection.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 23:19:16 +01:00
2cff4c8544 0.1.4 v0.1.4 2025-11-22 17:32:56 +01:00
00499732c4 Add optional info field to offers
- Add info field to Offer and CreateOfferRequest types
- Validate info field: optional, max 128 characters
- Include info field in all public API responses
- Update README with info field documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 17:32:56 +01:00
341d043358 0.1.3 v0.1.3 2025-11-22 16:05:36 +01:00
23c27d4509 Add custom peer ID support to register endpoint
- Update /register endpoint to accept optional custom peer ID
- Add validation: 1-128 chars, non-empty, must be unique
- Return 409 Conflict if peer ID already in use
- Remove outdated API.md documentation
- Update README.md with new register endpoint format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 16:05:25 +01:00
69f2bea967 0.1.2 v0.1.2 2025-11-17 22:09:10 +01:00
044dc85112 Add npm version badge to README 2025-11-17 21:45:31 +01:00
027b7d7fd9 Update README to include live API link for rondevu-server 2025-11-17 21:44:13 +01:00
a7b1d34000 Update live demo link in README to use ronde.vu domain 2025-11-17 21:43:09 +01:00
0a2eb35c38 Add secret column to offers table in migration
- Enables the protection of offers with a secret
- Answerers must provide the secret to interact with protected offers
2025-11-17 21:42:00 +01:00
7ca42c42aa Expand README with links to related repositories and NPM packages 2025-11-17 21:41:55 +01:00
8a65626225 Update README with secret field documentation
- Document secret parameter in POST /offers endpoint
- Document secret parameter in POST /offers/:offerId/answer endpoint
- Document hasSecret flag in GET /offers/by-topic response
- Add Protected Offers feature to features list

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 22:04:12 +01:00
f58e6e1014 Add startsWith filter to topics endpoint
Added optional startsWith parameter to GET /topics endpoint:
- Filters topics by prefix using SQL LIKE operator
- Updated storage interface and implementations (SQLite & D1)
- Added query param documentation
- Returns startsWith in response when used

Version bumped to 0.1.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 20:41:47 +01:00
1a3976ccbc Remove unused heartbeat endpoint and storage method
- Removed PUT /offers/:offerId/heartbeat endpoint
- Removed updateOfferLastSeen() from storage interface and implementations
- last_seen column is still in DB but not used for cleanup
- Cleanup only uses expires_at, so heartbeat was non-functional

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 18:32:37 +01:00
b9bf0a5593 Update package-lock.json 2025-11-16 17:38:05 +01:00
fe912e6a94 feat: Implement content-based offer IDs with SHA-256 hashing
- Added hash-id.ts utility for SHA-256 content hashing
- Offer IDs now generated from hash of {sdp, topics} (sorted)
- Removed peerId from hash (inferred from authentication)
- Server generates deterministic IDs for idempotent offer creation
- Updated SQLite and D1 storage implementations
- Removed optional id field from CreateOfferRequest
- Same offer content always produces same ID

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 16:34:28 +01:00