Amber, for developers
Through Amber, an agent cannot irreversibly delete or overwrite data. Edits and deletes are reversible by construction; truly removing data requires out-of-band human approval.
How the guarantee holds
The trust boundary is a capability the agent lacks, not a rule it’s asked to follow. The API exposes two token scopes:
- Agent scope — create-table, insert, update, soft-delete, restore,
query, request-purge. Every write is an
INSERTof a new version row; the old bytes never leave.queryruns on a connection opened read-only at the SQLite level, so destructive SQL smuggled into it fails with “attempt to write a readonly database.” No verb here removes data. - Operator scope — approve / reject / settle. The only code path that
issues
DELETE/DROPruns only against a purge request that verifies as approved.
agent ── insert / update / delete / restore ──▶ append-only history (reversible)
│ query (read-only connection) ─────────▶ current-state views
└── request-purge ──▶ human approval ──▶ verified approved ──▶ real removal
The authoritative history is an append-only op log: each row version is a
content-addressed op carrying the whole row. Each logical table exposes a view of the
latest non-deleted row — query reads the views, never the op log.
Getting access
Amber is in early access: databases are provisioned by hand, free of charge. Email
aws-amber@stackwell.tech
and you’ll get back a database with two bearer tokens — an agent token
(agt_…) for the AI session and an operator token (opr_…) for
you. Tokens are shown once and stored only as hashes; a token reaches only its own
database, so tenant isolation is structural.
API surface
Base URL: https://amberkeeps.io. Authenticate with
Authorization: Bearer <token>.
Agent token
POST /v1/tables— create a tablePOST /v1/tables/:table/rows— insert a rowPATCH /v1/tables/:table/rows/:rid— update (a new version; the old one is kept)POST /v1/tables/:table/rows/:rid/soft-delete— reversible deletePOST /v1/tables/:table/rows/:rid/restore— undo a soft-deletePOST /v1/query— read-only SQL against the current-state viewsPOST /v1/purge-requests— request permanent removal (destroys nothing by itself)
Operator token
POST /v1/purge-requests/:id/approve— approve a purge (local mode)POST /v1/purge-requests/:id/reject— reject it
Either token may GET /v1/purge-requests and
POST /v1/purge-requests/:id/settle (settle only carries out a decision that
was already made — it cannot itself approve).
Scopes are strict and disjoint: an agent token is 403 on approve; an
operator token is 403 on insert; a DELETE smuggled into
/v1/query fails at the SQLite layer.
Examples
# the AI session — agent scope
export AMBER=https://amberkeeps.io
export AGT=agt_...
curl -H "Authorization: Bearer $AGT" -H 'content-type: application/json' \
-X POST $AMBER/v1/tables \
-d '{"name":"users","columns":[{"name":"name","type":"TEXT"},{"name":"role","type":"TEXT"}]}'
curl -H "Authorization: Bearer $AGT" -H 'content-type: application/json' \
-X POST $AMBER/v1/tables/users/rows \
-d '{"data":{"name":"Ada","role":"eng"}}'
curl -H "Authorization: Bearer $AGT" -H 'content-type: application/json' \
-X PATCH $AMBER/v1/tables/users/rows/1 \
-d '{"changes":{"role":"staff eng"}}'
curl -H "Authorization: Bearer $AGT" -H 'content-type: application/json' \
-X POST $AMBER/v1/query \
-d '{"sql":"SELECT rid, name, role FROM users"}'
# permanent removal: the agent can only ask
curl -H "Authorization: Bearer $AGT" -H 'content-type: application/json' \
-X POST $AMBER/v1/purge-requests \
-d '{"kind":"row","table":"users","rid":2,"reason":"cleanup"}'
# the human terminal — operator scope
export OPR=opr_...
curl -H "Authorization: Bearer $OPR" $AMBER/v1/purge-requests
curl -H "Authorization: Bearer $OPR" -X POST $AMBER/v1/purge-requests/1/approve
Approvals
Amber always keeps a local enforcement ledger of purge requests; a per-database provider governs the decision. The DELETE only runs once the decision verifies as approved, whichever provider decided it.
- Local (default) — a human decides via the operator endpoints above.
- Granite — the purge is filed with
Granite, the approval service, and you approve
it in your Granite inbox (web or Android). The operator approve/reject endpoints return
409(decide in Granite);POST /v1/purge-requests/:id/settledrives enforcement after Amber verifies the approval with Granite directly.
Limits
Refusals are loud, never silent. Defaults (tunable per deployment):
- Rate limit: 60 requests/min per token →
429withRetry-After - Query budget: 5 s wall clock (the statement is interrupted) →
408 - Query result cap: 10,000 rows →
422 - Request body cap: 1 MiB →
413
Threat model, honestly
Amber defends against a fallible or prompt-injected agent confined to these tools: nothing reachable with an agent token can irreversibly destroy data. It does not defend against an attacker with access to the underlying infrastructure — that is a different threat model. Operationally, tenant data lives on encrypted storage with daily backups (35-day retention); see the privacy policy for exactly what is stored and for how long.