Implement ecosystem contract v2: capabilities, shared ledgers, attachments
- GET /v1/capabilities and the extended sync entity allowlist (§0, §1). - Shared ledgers (§7): ledgers, members and hashed one-time invite codes; X-Balance-Ledger selects the space on /v1/sync and /v1/attachments with its own records, change stream and cursor. Personal space keeps the v1 tables and behaviour; the migration only adds tables. - Attachments (§8): SHA-256, size and media type checks, blob files in BALANCE_ATTACHMENTS_DIR (default: attachments next to the database), garbage collection of unreferenced blobs with a grace period for fresh uploads, and ledger deletion removes its blobs. - Tests for every new endpoint and error code; API, sync, data model, security, deployment and README docs updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
10272f3851
commit
dfb4fe4e1a
17 files changed
+2496
-56
No files matched your search
@@ -76,6 +76,7 @@ The browser session uses `HttpOnly`, `SameSite=Strict` cookies. Native applicati
|
||||
| `BALANCE_ACCESS_TTL` | `15m` | Access-token lifetime |
|
||||
| `BALANCE_REFRESH_TTL` | `720h` | Refresh-token lifetime |
|
||||
| `BALANCE_CORS_ORIGINS` | empty | Comma-separated browser origins; native apps do not need CORS |
|
||||
| `BALANCE_ATTACHMENTS_DIR` | `attachments` next to `BALANCE_DB_PATH` | Directory for attachment files (receipt photos); Docker: `/data/attachments` |
|
||||
|
||||
## HTTPS
|
||||
|
||||
@@ -99,15 +100,24 @@ Then enter `https://balance.example.com` in each application's Settings → Serv
|
||||
| `POST` | `/v1/auth/refresh` | Rotate session tokens |
|
||||
| `POST` | `/v1/auth/logout` | Revoke refresh token |
|
||||
| `GET` | `/v1/me` | Current account |
|
||||
| `POST` | `/v1/sync` | Push and pull changes |
|
||||
| `POST` | `/v1/sync` | Push and pull changes (`X-Balance-Ledger` selects a shared ledger) |
|
||||
| `GET` | `/v1/capabilities` | Contract version and features (no authorization) |
|
||||
| `GET`, `POST` | `/v1/ledgers` | List / create shared ledgers |
|
||||
| `PATCH`, `DELETE` | `/v1/ledgers/{id}` | Rename / delete a ledger (owner) |
|
||||
| `POST` | `/v1/ledgers/{id}/invites` | One-time invite code (owner) |
|
||||
| `POST` | `/v1/ledgers/join` | Join a ledger by code |
|
||||
| `DELETE` | `/v1/ledgers/{id}/members/{userId}` | Remove a member or leave |
|
||||
| `PUT`, `GET` | `/v1/attachments/{sha256}` | Upload / download attachment bytes |
|
||||
|
||||
The sync endpoint accepts transactions, custom categories and monthly budgets. The server isolates records by account, stores an append-only change cursor, and resolves competing edits with last-write-wins timestamps. Current Apple and Android clients use push-only requests followed by paginated pulls; update the server and applications together.
|
||||
The server implements ecosystem contract v2 ([docs/CONTRACT_V2.md](docs/CONTRACT_V2.md)).
|
||||
The sync endpoint accepts transactions, categories, budgets, accounts, transfers,
|
||||
recurring operations, goals, debts, exchange rates, settings and attachment metadata. The server isolates records by account, stores an append-only change cursor, and resolves competing edits with last-write-wins timestamps. Current Apple and Android clients use push-only requests followed by paginated pulls; update the server and applications together.
|
||||
|
||||
## Operational notes
|
||||
|
||||
- Registration can be disabled after the required accounts have been created.
|
||||
- Passwords are stored as salted Argon2id hashes. Refresh tokens are stored only as SHA-256 hashes.
|
||||
- Back up the SQLite database and its `-wal`/`-shm` files together, or stop the server before copying the database.
|
||||
- Back up the SQLite database and its `-wal`/`-shm` files together with the attachments directory, or stop the server before copying them.
|
||||
- Changing `BALANCE_JWT_SECRET` invalidates access tokens. Existing refresh sessions remain valid and will receive new access tokens after refresh.
|
||||
- The in-process authentication limiter allows 30 authentication requests per IP per minute. Use a reverse proxy for stronger public rate limiting and request logging.
|
||||
|
||||
|
||||
Reference in new issue
Block a user