Skip to content

Commit 3280931

Browse files
committed
Update docs: retention HTTP routes (CL-6288)
AGENTS.md/ARCHITECTURE.md route lists, docs/RETENTION.md (route table, grant actions, ownership gate, sweepEphemeral decision), and CHANGELOG.
1 parent 0242904 commit 3280931

4 files changed

Lines changed: 67 additions & 4 deletions

File tree

‎AGENTS.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ CI runs `typecheck` + `test` — both must pass before any push.
2222
- `src/index.ts` — public surface: `createMemory` (optional `app` registers HTTP), `registerMemoryRoutes`
2323

2424
- `src/mount-config.ts` / `src/config.ts` — mount config + engine config
25-
- `src/routes/` — Hono routes (`add`, `search`, `list`)
25+
- `src/routes/` — Hono routes (`add`, `search`, `list`, `feed`, retention
26+
`forget`/`purge`/`retention-class`)
2627
- `src/tools/` — Interchange `defineTool` factories (`@corbits/memory/tools`);
2728
HTTP clients for mounted routes (env credentials; no in-process plane)
2829
- `src/services/` — capture / search / transform internals (not public verbs)

‎ARCHITECTURE.md‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,23 @@ exposes the same three verbs.
8585
authority/recency → MMR); optional live `SourceProvider` merge (fail-soft).
8686
- `GET /api/tenants/:tenantId/memory/list` — recent documents, same grant-tag filter as local
8787
search.
88-
89-
Returns an in-process `Memory` (`add`, `search`, `list`, `close`) for host
90-
workers and ingestion modules that already resolved identity.
88+
- `POST /api/tenants/:tenantId/memory/documents/:documentId/forget` — tombstone
89+
(grant `memory:forget`; creator-only, see below).
90+
- `POST /api/tenants/:tenantId/memory/documents/:documentId/purge` — hard
91+
delete (grant `memory:purge`; creator-only; irreversible).
92+
- `POST /api/tenants/:tenantId/memory/versions/:versionId/retention-class` —
93+
set retention class (grant `memory:forget`; creator-only).
94+
95+
Forget and purge are deliberately separate routes and separate grant actions
96+
(never one route with a boolean flag) — a host wiring a "forget this" button
97+
cannot accidentally wire up permanent deletion. `sweepEphemeral` (TTL
98+
auto-deprecation) is **not** HTTP-routed: it is a maintenance sweep a host
99+
schedules on its own cron, not a user action; call it in-process against the
100+
returned `Memory`. See docs/RETENTION.md.
101+
102+
Returns an in-process `Memory` (`add`, `search`, `list`, `close`, plus the
103+
optional retention writes) for host workers and ingestion modules that
104+
already resolved identity.
91105

92106
**Agent tools live in this package** as thin HTTP clients
93107
(`@corbits/memory/tools` / `interchange.tools`): `defineTool` factories that

‎CHANGELOG.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- Retention HTTP routes (CL-6288): `POST …/memory/documents/:documentId/forget`
13+
(tombstone, grant `memory:forget`), `POST …/memory/documents/:documentId/purge`
14+
(hard delete, grant `memory:purge`), and
15+
`POST …/memory/versions/:versionId/retention-class` (grant `memory:forget`).
16+
Forget and purge are separate routes with separate grant actions — never one
17+
route with a boolean flag — and both are refused with 403 unless the caller
18+
is the document/version's creator, independent of any share grant that lets
19+
them merely see it. `sweepEphemeral` stays off the HTTP surface (maintenance
20+
sweep, not a user action); a host schedules it on its own cron against the
21+
in-process `Memory`. New `memory:forget` / `memory:purge` grant requirements
22+
(`source: "creator"`) and `capabilityIdsForSurface()` so distiller/tools
23+
installs no longer pick up routes-only capabilities by accident.
1224
- `RouteDeps.callerResolver` / `createMemory({ callerResolver })` — an
1325
optional host-supplied resolver from a request to a `{ tenantId,
1426
principalId }` scope, for a caller that never goes through the host's

‎docs/RETENTION.md‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,39 @@ versions intentionally (ops / audit). Hard-delete is a separate explicit
3030
verb — TTL never hard-deletes.
3131

3232
Service module: `src/services/retention.ts`.
33+
34+
## HTTP surface (CL-6288)
35+
36+
| Route | Grant action | Plane verb |
37+
| --- | --- | --- |
38+
| `POST …/memory/documents/:documentId/forget` | `memory:forget` | `tombstoneDocument` |
39+
| `POST …/memory/documents/:documentId/purge` | `memory:purge` | `hardDeleteDocument` |
40+
| `POST …/memory/versions/:versionId/retention-class` | `memory:forget` | `setRetentionClass` |
41+
42+
`deprecateVersion` and `sweepEphemeral` have no route (see below).
43+
44+
**Tombstone vs. hard delete stay distinct verbs, distinct grants.** A UI
45+
offering "forget this" must never be one flag away from "shred this" by
46+
accident. `forget` (tombstone) is the reversible-in-principle, audit-keeping
47+
action; `purge` (hard delete) is the one that actually removes the row, has
48+
its own grant action, and is refused outright while a `durable`-class version
49+
on the document is untombstoned. A host can grant `forget` broadly (every
50+
user gets a "forget this" button) while keeping `purge` to an operator role.
51+
52+
**Ownership, not just visibility.** `memory:search`/a document's `accessTags`
53+
say who can *see* a document — never who may forget or purge it. Every
54+
retention route additionally checks that the caller is the document's
55+
creator (`created_by_principal_id` — the document's first version for
56+
`forget`/`purge`, the specific version's own creator for `retention-class`),
57+
independent of any share grant. A peer who can search a shared document gets
58+
403 on `forget`/`purge`/`retention-class` for it. See
59+
`src/services/retention-ownership.ts` and the ownership tests in
60+
`src/memory.test.ts` / `src/routes/routes.test.ts`.
61+
62+
**`sweepEphemeral` stays off the HTTP surface.** It is a maintenance sweep —
63+
"deprecate every ephemeral version past its TTL for this tenant" — not
64+
something a single user requests about their own data, and it has no natural
65+
per-caller grant (it does not take a `principalId` and touches every
66+
matching row tenant-wide). A host that wants it schedules a cron job calling
67+
`memory.sweepEphemeral({ tenantId })` in-process (the returned `Memory`
68+
already exposes it); the engine stays cron-free per `ARCHITECTURE.md`.

0 commit comments

Comments
 (0)