antpath 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -4
- package/dist/cli.mjs +367 -0
- package/dist/cli.mjs.sha256 +1 -0
- package/dist/client.js +4 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/platform/client.d.ts +138 -7
- package/dist/platform/client.js +98 -2
- package/dist/platform/client.js.map +1 -1
- package/dist/providers/known-events.d.ts +60 -0
- package/dist/providers/known-events.js +64 -0
- package/dist/providers/known-events.js.map +1 -0
- package/dist/run/controller.d.ts +4 -1
- package/dist/run/controller.js +103 -13
- package/dist/run/controller.js.map +1 -1
- package/dist/template/index.d.ts +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/utils/events.d.ts +21 -0
- package/dist/utils/events.js +97 -18
- package/dist/utils/events.js.map +1 -1
- package/docs/credentials.md +84 -2
- package/docs/events.md +129 -0
- package/docs/skills.md +2 -0
- package/package.json +5 -2
- package/references/architecture-decisions.md +110 -64
- package/references/implementation-plan.md +66 -44
|
@@ -20,9 +20,9 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
20
20
|
- Minimal tenant-scoped dashboard metadata for monitoring run execution.
|
|
21
21
|
- Durable worker recovery after restarts, deploys, crashes, or missed notifications.
|
|
22
22
|
- Horizontal worker scaling without duplicate run ownership.
|
|
23
|
-
- Provider cleanup by default after terminal states, with optional retention when configured per-request or per-deployment.
|
|
24
|
-
- BYO provider key custody with encrypted
|
|
25
|
-
-
|
|
23
|
+
- Provider cleanup by default after terminal states, with optional retention of Claude-side resources when configured per-request or per-deployment.
|
|
24
|
+
- Per-session BYO provider key custody: keys arrive inline with each run, are encrypted in Supabase Vault for the lifetime of that single run, and are deleted at cleanup. Persistent workspace-level provider key custody is backlog.
|
|
25
|
+
- Unconditional capture of every session-scoped Claude artifact into private Supabase Storage, bounded only by the workspace storage cap.
|
|
26
26
|
- Programmatic SDK access for submitting and observing platform runs.
|
|
27
27
|
- Test-driven implementation across SDK, dashboard, worker, database, storage, and provider adapters.
|
|
28
28
|
|
|
@@ -34,6 +34,8 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
34
34
|
- No direct browser access to provider APIs.
|
|
35
35
|
- No direct browser Supabase data access.
|
|
36
36
|
- No raw prompt, raw model output, raw provider event payload, MCP credential, provider key, or output file content in normal application tables.
|
|
37
|
+
- No persistent workspace-level provider connections (no "saved Anthropic keys"). Every run carries its own secrets bundle; nothing about the user's Anthropic key persists past run cleanup. Persistent BYO custody is backlog.
|
|
38
|
+
- No user-configurable output capture: every artifact Claude exposes on the run's session is captured. There are no globs, no `capture: false`, no per-file or per-run user-visible caps.
|
|
37
39
|
- No provider Agent/Environment caching in MVP.
|
|
38
40
|
- No provider webhooks in MVP.
|
|
39
41
|
- No Supabase Realtime in MVP.
|
|
@@ -44,7 +46,7 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
44
46
|
| Area | Decision |
|
|
45
47
|
| --- | --- |
|
|
46
48
|
| Product surface | Platform plus SDK. |
|
|
47
|
-
| Repository | Convert the repository to
|
|
49
|
+
| Repository | Convert the repository to a pnpm TypeScript workspace. |
|
|
48
50
|
| SDK location | Move the existing SDK package to `packages/sdk`. |
|
|
49
51
|
| SDK role | The SDK submits runs and observes status, metadata, outputs, cancellation, and deletion through the platform API. |
|
|
50
52
|
| Dashboard | Build a first-party minimal dashboard. |
|
|
@@ -59,8 +61,8 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
59
61
|
| Service credentials | Supabase service-role credentials are server/worker only. |
|
|
60
62
|
| SDK auth | SDK uses hashed, workspace-scoped API tokens. Dashboard uses Auth.js sessions. |
|
|
61
63
|
| API token attribution | Token-authenticated runs are attributed to the token creator at submission time unless a future service-account model is introduced. |
|
|
62
|
-
| Provider key custody |
|
|
63
|
-
| Secret lifetime | Worker resolves
|
|
64
|
+
| Provider key custody | BYO Anthropic key arrives inline with every run submission. The BFF stores the secrets bundle in Supabase Vault, attaches the secret id to the run row, and deletes the Vault entry as part of cleanup. Persistent workspace-level provider connections are backlog. |
|
|
65
|
+
| Secret lifetime | Worker resolves the per-run secrets bundle once per claimed lifecycle step, keeps the decoded payload only in memory for that step, and drops it before lease release. The Vault entry itself is destroyed when the run reaches terminal cleanup. |
|
|
64
66
|
| Provider resources | Create provider Agent, Environment, Vault/Credential, Session, and file resources per run in MVP. |
|
|
65
67
|
| Provider resource caching | Backlog; later cache Agent/Environment by Template/config hash. |
|
|
66
68
|
| Worker wakeup | Poll due rows from the runs table. Postgres `NOTIFY` is a latency optimization only. |
|
|
@@ -72,12 +74,13 @@ This supersedes the earlier SDK-only MVP boundary.
|
|
|
72
74
|
| MCP approvals | Disallow approval-required tools. Worker must not approve tools or return custom tool results. |
|
|
73
75
|
| Template boundary | Templates are code-first, secret-free snapshots with stable hashes. |
|
|
74
76
|
| Idempotency | Run submission idempotency is scoped by `(workspace_id, idempotency_key)` and request hash. Same key and same hash returns the existing run; same key and different hash returns conflict. |
|
|
75
|
-
| Quotas | Enforce workspace
|
|
76
|
-
| Run caps | Plan-based caps cover duration, concurrency,
|
|
77
|
+
| Quotas | Enforce workspace storage and concurrency quotas. Storage is the only user-visible cap on output capture. |
|
|
78
|
+
| Run caps | Plan-based caps cover duration, concurrency, polling, retries, and workspace storage. There are no user-visible per-file or per-run output caps. |
|
|
77
79
|
| Operational config | Exact cap/tier values are environment-configurable defaults with conservative fallbacks and run-level snapshots. |
|
|
78
|
-
|
|
|
80
|
+
| Boot-time env validation | Worker and dashboard validate required environment variables at startup through a shared role-scoped helper and exit/abort with an aggregated error when any are missing. No silent defaults for identity, credentials, or required URLs. |
|
|
81
|
+
| Output capture | Unconditional. Every file Claude exposes on the run's session is captured into private Supabase Storage. The workspace storage cap is the only user-visible quota; capture failures (quota exceeded, download failed) are persisted as `output_capture_failures` rows and surfaced in the dashboard alongside successful outputs. |
|
|
79
82
|
| Output access | BFF returns signed links only after workspace authorization. |
|
|
80
|
-
| Cleanup | Clean up Claude provider resources by default after terminal state and output capture; explicit run policy or worker default can retain
|
|
83
|
+
| Cleanup | Clean up Claude provider resources by default after terminal state and output capture; explicit run policy or worker default can retain Claude-side resources for inspection. The per-run Vault secret is always deleted at cleanup regardless of the Claude-side retention knob. |
|
|
81
84
|
| Delete semantics | User delete is soft/pending while execution, cleanup, or storage deletion is active. Hard purge happens only after cleanup/storage deletion succeeds. |
|
|
82
85
|
| Retention | Run metadata and stored outputs remain until user deletion in MVP. |
|
|
83
86
|
| Realtime | Defer from MVP; use BFF-mediated refresh/polling first. Revisit with custom Supabase JWT/RLS or a server-mediated realtime bridge. |
|
|
@@ -125,35 +128,44 @@ packages/
|
|
|
125
128
|
|
|
126
129
|
### Identity and tenancy
|
|
127
130
|
|
|
128
|
-
- `users`
|
|
129
|
-
- Auth.js
|
|
131
|
+
- Auth.js adapter tables: `users`, `accounts`, `sessions`, and `verification_token`
|
|
132
|
+
- Auth.js owns OAuth account linking and email magic-link verification persistence in the same Supabase Postgres database.
|
|
133
|
+
- Active application sessions use Auth.js JWT strategy; the `sessions` table exists for adapter compatibility but is not the active session store.
|
|
134
|
+
- Auth.js OAuth tokens remain confined to Auth.js adapter tables and never become antpath provider credentials.
|
|
135
|
+
- `app_users`
|
|
136
|
+
- Antpath-owned user identity row linked to Auth.js `users.id` through `auth_user_id`.
|
|
137
|
+
- Workspace membership, run attribution, API-token ownership, disable state, and JWT token-version checks use `app_users.id`.
|
|
138
|
+
- `disabled_at` and `token_version` are checked by privileged BFF routes to reject disabled or stale JWT users.
|
|
130
139
|
- No provider secrets.
|
|
131
140
|
- `workspaces`
|
|
132
141
|
- Tenant boundary.
|
|
133
142
|
- Plan, quota, status, and retention settings.
|
|
134
143
|
- `workspace_memberships`
|
|
135
|
-
- Workspace,
|
|
144
|
+
- Workspace, `app_user_id`, role, and membership status.
|
|
136
145
|
- Every dashboard/API operation must prove membership/scope.
|
|
137
146
|
- `api_tokens`
|
|
138
147
|
- Workspace-scoped SDK credentials.
|
|
139
|
-
- Store hashed token material only, plus scopes,
|
|
148
|
+
- Store hashed token material only, plus scopes, `creator_app_user_id`, last-used timestamp, and revoked timestamp.
|
|
140
149
|
- API-token submissions freeze the attributed user on the run row at creation time.
|
|
141
150
|
|
|
142
|
-
###
|
|
151
|
+
### Per-run secrets
|
|
143
152
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
There is no `provider_connections` table in MVP. Every run carries its own secrets.
|
|
154
|
+
|
|
155
|
+
- The submission contract has a top-level `secrets` block: `{ anthropic: { apiKey }, mcpServers?: [{ name, url, headers? }], skills?: [...] }`. The shared parser enforces that **no other field** carries secret-bearing keys; the `secrets` block is the single allowlisted carrier and never serializes into normal metadata, logs, events, audit rows, or stored snapshots.
|
|
156
|
+
- The dashboard BFF stores the JSON-encoded bundle in Supabase Vault at submission time and writes the resulting `vault_secret_id` into `runs.execution_secret_id`. The bundle is the only durable trace of the user's Anthropic key, MCP credentials, and skill references for that run.
|
|
157
|
+
- Worker code reads `runs.execution_secret_id` once per claimed step, decodes the bundle in memory, builds the Claude provider, and drops the decoded values before lease release.
|
|
158
|
+
- At terminal cleanup the worker deletes the Vault entry and clears `runs.execution_secret_id`. Cleanup of the Vault secret happens regardless of the Claude-side retention knob.
|
|
159
|
+
- If the user revokes (rotates) the Anthropic key on Anthropic's side mid-run, antpath has no knowledge of this until the next provider call fails. Active runs surface a `tenant_permanent` failure with a redacted reason; cleanup proceeds with the previously vaulted key while it remains valid and gives up gracefully if it does not.
|
|
149
160
|
|
|
150
161
|
### Runs
|
|
151
162
|
|
|
152
163
|
- `runs`
|
|
153
164
|
- User-visible logical run.
|
|
154
|
-
- Workspace,
|
|
165
|
+
- Workspace, `creator_app_user_id`, template snapshot/hash, status, lifecycle phase, plan caps, timestamps.
|
|
155
166
|
- Submission idempotency key and request hash.
|
|
156
167
|
- Unique constraint: `(workspace_id, idempotency_key)`.
|
|
168
|
+
- `execution_secret_id` — Supabase Vault secret id for the per-run secrets bundle (Anthropic key + MCP credentials + skill references). Cleared at cleanup.
|
|
157
169
|
- Lease fields: `lease_owner`, `lease_token`, `lease_expires_at`, `attempt_count`, `next_check_at`, `priority`.
|
|
158
170
|
- Cancellation/deletion fields: `cancel_requested_at`, `pending_delete_at`, `deleted_at`.
|
|
159
171
|
- Provider observation cursor/watermark where available.
|
|
@@ -174,24 +186,34 @@ packages/
|
|
|
174
186
|
|
|
175
187
|
- `output_objects`
|
|
176
188
|
- Workspace/run owner, storage bucket/path, size, checksum if available, content type, provider file id.
|
|
189
|
+
- One row per artifact successfully captured from the run's session into private Supabase Storage.
|
|
177
190
|
- Used for quota accounting and signed-link generation.
|
|
191
|
+
- `output_capture_failures`
|
|
192
|
+
- Workspace/run owner, provider file id, filename, byte size as reported by the provider, reason (`workspace_quota_exceeded`, `download_failed`, etc.), redacted error message.
|
|
193
|
+
- Surfaced in the dashboard alongside `output_objects` so the user can see exactly which session artifacts didn't make it to storage and why.
|
|
178
194
|
- `cleanup_attempts`
|
|
179
195
|
- Per-resource cleanup action, status, retry count, redacted error code/message, timestamps.
|
|
180
196
|
- `usage_ledger`
|
|
181
197
|
- Token/cost/storage attribution by workspace and attributed user.
|
|
182
198
|
- Written transactionally with source event/output rows to avoid drift.
|
|
199
|
+
- `audit_logs`
|
|
200
|
+
- Safe security audit trail for sign-in user upsert, API-token changes, run cancellation/deletion, signed-link creation, and cleanup failures.
|
|
201
|
+
- Stores safe identifiers, action names, redacted errors, and non-secret metadata only.
|
|
202
|
+
- `rate_limit_buckets`
|
|
203
|
+
- Durable token-bucket-style counters for abuse-sensitive BFF/API operations.
|
|
204
|
+
- Used before side effects for run submission, run cancellation/deletion, signed-link creation, API-token creation/revocation, and email-auth throttling where practical.
|
|
183
205
|
|
|
184
206
|
## Run lifecycle
|
|
185
207
|
|
|
186
|
-
1. SDK/dashboard submits a run to the BFF.
|
|
208
|
+
1. SDK/dashboard submits a run to the BFF. The submission carries a top-level `secrets` bundle (Anthropic key, optional MCP credentials, optional skill references) plus the non-secret template/variables/cleanup payload.
|
|
187
209
|
2. BFF validates Auth.js session or SDK API token.
|
|
188
210
|
3. BFF resolves active workspace membership/scope.
|
|
189
|
-
4. BFF validates Template/request shape, plan limits, and idempotency key/request hash.
|
|
190
|
-
5. BFF writes a `runs` row with execution-affecting cap values snapshotted from plan/env defaults.
|
|
211
|
+
4. BFF validates Template/request shape, plan limits, and idempotency key/request hash. The shared parser rejects every secret-bearing key outside the allowlisted `secrets` block.
|
|
212
|
+
5. BFF stores the JSON-encoded `secrets` bundle in Supabase Vault and writes a `runs` row with execution-affecting cap values snapshotted from plan/env defaults plus `execution_secret_id` pointing at the Vault entry.
|
|
191
213
|
6. BFF emits Postgres `NOTIFY` for fast wakeup.
|
|
192
214
|
7. Worker claims due runs with row locking, `SKIP LOCKED`, lease token, and lease expiry.
|
|
193
|
-
8. Worker resolves the
|
|
194
|
-
9. Worker validates platform constraints: no approval-required tools, no custom antpath-executed tools
|
|
215
|
+
8. Worker resolves the per-run secrets bundle from Supabase Vault for the claimed step.
|
|
216
|
+
9. Worker validates platform constraints: no approval-required tools, no custom antpath-executed tools.
|
|
195
217
|
10. Worker pre-journals intended provider resources.
|
|
196
218
|
11. Worker creates provider resources and records provider IDs immediately after successful calls.
|
|
197
219
|
12. Worker creates provider session and sends the initial user event.
|
|
@@ -199,8 +221,8 @@ packages/
|
|
|
199
221
|
14. Worker later reclaims the run and polls provider session status plus event list.
|
|
200
222
|
15. Worker stores only metadata events and usage, deduped by provider event id.
|
|
201
223
|
16. Before every side effect, worker checks lease token and cancellation/deletion requests.
|
|
202
|
-
17. On terminal provider state or antpath timeout, worker
|
|
203
|
-
18. Worker cleans up provider resources by default, or records them as retained when the run/deployment policy asks to retain them.
|
|
224
|
+
17. On terminal provider state or antpath timeout, worker lists session-scoped files via the Claude Files API and unconditionally captures every file the workspace storage quota still has room for, persisting `output_capture_failures` rows for any file that would exceed the cap.
|
|
225
|
+
18. Worker cleans up Claude-side provider resources by default, or records them as retained when the run/deployment policy asks to retain them. The per-run Vault entry is deleted regardless.
|
|
204
226
|
19. Worker marks the run terminal and releases the lease.
|
|
205
227
|
20. Dashboard/SDK read tenant-scoped metadata and signed output links through BFF APIs.
|
|
206
228
|
|
|
@@ -259,51 +281,59 @@ BFF/server actions must:
|
|
|
259
281
|
- return only metadata allowed by the user's role/scope;
|
|
260
282
|
- keep Supabase service-role credentials out of browser bundles.
|
|
261
283
|
|
|
284
|
+
Auth.js and antpath authorization stay separate in the database. Auth.js owns `users`, `accounts`, `sessions`, and `verification_token`; antpath owns `app_users` and links each row to Auth.js `users.id`. JWT callbacks expose only safe identity fields such as `app_user_id` and token version. BFF routes must load the current `app_users` row and reject disabled users or stale token versions before privileged actions.
|
|
285
|
+
|
|
286
|
+
All platform and Auth.js tables have RLS enabled and `anon`/`authenticated` table privileges revoked. This is a defense-in-depth guard against accidental Supabase Data API exposure; direct `pg` repository code remains the primary BFF/worker authorization boundary for this phase.
|
|
287
|
+
|
|
262
288
|
Supabase Realtime is backlog until antpath either mints short-lived Supabase-compatible JWTs with RLS policies or exposes a server-mediated realtime bridge.
|
|
263
289
|
|
|
264
290
|
## Secret handling
|
|
265
291
|
|
|
266
|
-
- Provider keys
|
|
267
|
-
- MCP credentials
|
|
268
|
-
- Normal app tables store only secret
|
|
269
|
-
- Worker resolves
|
|
270
|
-
-
|
|
292
|
+
- Provider keys arrive inline with each submission and are stored in Supabase Vault tied to a single run via `runs.execution_secret_id`.
|
|
293
|
+
- MCP credentials and any skill payloads referenced by the run live alongside the provider key inside the same per-run Vault bundle.
|
|
294
|
+
- Normal app tables store only the Vault secret id, not decoded provider keys, MCP credentials, or skill payloads.
|
|
295
|
+
- Worker resolves the decoded bundle only for a claimed lifecycle step and drops the decoded values before lease release.
|
|
296
|
+
- The shared submission parser keeps an allowlist of exactly one carrier (`secrets`) and rejects every other field that looks like a secret-bearing key (`apiKey`, `accessToken`, `password`, `mcpCredentials`, etc.).
|
|
297
|
+
- Secret values use explicit redacted wrappers in code so they cannot serialize into logs, metrics, errors, events, or fixtures.
|
|
271
298
|
- Secret redaction applies to logs, errors, run metadata, provider events, tests, fixtures, and docs.
|
|
272
|
-
-
|
|
273
|
-
-
|
|
299
|
+
- The per-run Vault entry is deleted as part of run cleanup. Cleanup of the Vault entry happens regardless of the Claude-side retention knob.
|
|
300
|
+
- If a provider key is revoked or rotated on the provider side mid-run, the next provider call fails with a `tenant_permanent` error. The worker fails the affected run, attempts cleanup with whatever credentials are still valid, and surfaces a redacted reason on the dashboard.
|
|
301
|
+
- There is no platform-side rotation/revocation flow because there is no persistent provider connection. To "rotate" the user simply submits the next run with a different key.
|
|
274
302
|
|
|
275
303
|
## Output storage and quotas
|
|
276
304
|
|
|
305
|
+
Output capture is unconditional. Every artifact the user's Claude session exposes is downloaded to private Supabase Storage. There are no user-facing knobs to opt out, no globs, and no per-file or per-run user-visible caps.
|
|
306
|
+
|
|
277
307
|
Output flow:
|
|
278
308
|
|
|
279
|
-
1. Worker lists
|
|
280
|
-
2.
|
|
281
|
-
3.
|
|
282
|
-
4.
|
|
283
|
-
5.
|
|
284
|
-
6.
|
|
285
|
-
7. Worker cleans up provider resources by default, or retains them when explicitly configured.
|
|
286
|
-
8. Dashboard/SDK request signed links through the BFF.
|
|
309
|
+
1. Worker lists files scoped to the run's Claude session: `GET /v1/files?scope_id=<session_id>`. Each entry includes `id`, `filename`, and `size_bytes`.
|
|
310
|
+
2. For each file, before downloading, the worker checks `(workspace.storage_used_bytes + size_bytes)` against the workspace storage cap.
|
|
311
|
+
3. If the file would exceed the cap, the worker writes an `output_capture_failures` row (`reason = "workspace_quota_exceeded"`, with filename and reported byte size) and moves on without downloading.
|
|
312
|
+
4. Otherwise the worker downloads via `GET /v1/files/{file_id}/content` with a hard streaming safety cap so a malformed listing response cannot OOM the worker, uploads to private Supabase Storage at a workspace-scoped path, and writes an `output_objects` row.
|
|
313
|
+
5. Other download/upload failures (network, storage, unexpected size) are recorded as `output_capture_failures` with the redacted reason.
|
|
314
|
+
6. Dashboard/SDK request signed links through the BFF for any `output_objects` row.
|
|
287
315
|
|
|
288
316
|
Quota rules:
|
|
289
317
|
|
|
290
|
-
- Workspace
|
|
291
|
-
-
|
|
292
|
-
-
|
|
293
|
-
-
|
|
318
|
+
- Workspace storage cap is the only hard enforcement boundary. Default is generous (configurable via env, see [`environment-variables.md`](./environment-variables.md)).
|
|
319
|
+
- There is no per-user quota. Per-user attribution still freezes on the run row at submission time for billing/audit purposes, but it is not a quota dimension.
|
|
320
|
+
- There are no user-visible per-file or per-run caps. A worker-internal streaming safety cap protects against malformed listings.
|
|
321
|
+
- Capture failures are first-class data: the dashboard renders `<filename> (<size>) — skipped: workspace quota exceeded` next to successful outputs so the user can see exactly what didn't make it.
|
|
294
322
|
|
|
295
323
|
## Cleanup and reconciliation
|
|
296
324
|
|
|
297
|
-
Claude provider-resource cleanup is mandatory by default after terminal provider state and output capture so antpath does not leave behind provider state. Retention is opt-in
|
|
325
|
+
Claude provider-resource cleanup is mandatory by default after terminal provider state and output capture so antpath does not leave behind provider state. Retention is opt-in for **Claude-side resources only** through `cleanup.claudeSession = "retain"` or `ANTPATH_WORKER_CLAUDE_SESSION_CLEANUP_DEFAULT=retain`. Retained resources are recorded in `provider_resources.cleanup_status = retained` and remain reachable via the provider API. Antpath-side metadata, output objects, and the per-run Vault entry are not affected by this knob — Vault entries are always deleted at cleanup; metadata and outputs persist until the user explicitly deletes the run.
|
|
298
326
|
|
|
299
327
|
Explicit cleanup order:
|
|
300
328
|
|
|
301
|
-
1.
|
|
302
|
-
2.
|
|
303
|
-
3. Agent/archive.
|
|
304
|
-
4. Environment/archive or delete where allowed.
|
|
305
|
-
5.
|
|
306
|
-
6.
|
|
329
|
+
1. Capture session-scoped files into private Supabase Storage (with quota pre-check; failures persisted).
|
|
330
|
+
2. Provider session files / session where supported.
|
|
331
|
+
3. Agent / archive.
|
|
332
|
+
4. Environment / archive or delete where allowed.
|
|
333
|
+
5. Skills uploaded for this session and any other ephemeral provider resources created for this run.
|
|
334
|
+
6. Provider Vault/Credentials created on Claude's side for MCP wiring.
|
|
335
|
+
7. The antpath per-run Vault entry: `vault.deleteSecret(runs.execution_secret_id)`, then clear the column.
|
|
336
|
+
8. Local output metadata/storage only when the user deletes a run.
|
|
307
337
|
|
|
308
338
|
Cleanup properties:
|
|
309
339
|
|
|
@@ -319,10 +349,19 @@ Resource leak recovery:
|
|
|
319
349
|
- A reconciliation sweeper reviews unfinished intended rows, expired leases, and provider-listable resources tagged with antpath metadata.
|
|
320
350
|
- If provider create succeeded but provider id was not persisted before a crash, the sweeper attempts to match by deterministic name/metadata and attach the provider id for cleanup.
|
|
321
351
|
|
|
322
|
-
Workspace
|
|
352
|
+
Workspace deletion:
|
|
323
353
|
|
|
324
354
|
- Workspace deletion blocks new runs, requests cancellation for active runs, drains cleanup, deletes stored outputs, then purges metadata.
|
|
325
|
-
-
|
|
355
|
+
- There is no separate "provider key revocation" flow because there is no persistent provider key. Submitting the next run with a different key is the rotation mechanism.
|
|
356
|
+
|
|
357
|
+
## Audit, rate limits, quotas, and deletion
|
|
358
|
+
|
|
359
|
+
- Abuse-sensitive actions are rate-limited before provider, Vault, Storage, or mutation side effects where practical.
|
|
360
|
+
- Audit logs must never include provider keys, OAuth tokens, MCP credentials, raw prompts, raw outputs, signed URLs, or service-role credentials.
|
|
361
|
+
- Workspace storage quota is enforced before output download/upload when file size is known, with streaming hard caps for unknown-size files.
|
|
362
|
+
- App-user deletion is soft/disable-first; hard deletion must not break historical run, usage, output, or audit references.
|
|
363
|
+
- Workspace deletion is pending-first: block new submissions, mark active runs pending-delete, allow cleanup/storage deletion to drain, then purge only safe metadata.
|
|
364
|
+
- Recovery operations should be driven by explicit cleanup/deletion state and audit logs, not manual DB guessing.
|
|
326
365
|
|
|
327
366
|
## Run state model
|
|
328
367
|
|
|
@@ -364,6 +403,8 @@ Error classes:
|
|
|
364
403
|
- Database: Supabase Postgres.
|
|
365
404
|
- Storage: Supabase Storage private bucket.
|
|
366
405
|
- Secrets: Vercel/Railway environment variables plus Supabase Vault for tenant provider keys.
|
|
406
|
+
- CI: GitHub Actions with pnpm, default lint/test/build/package checks, and a separate local Supabase integration job.
|
|
407
|
+
- Local dev stack: `pnpm dev:stack` starts Supabase without printing local keys, validates local config, checks migrations, builds shared packages, and runs the dashboard and worker together. Missing Auth.js providers do not block local service startup unless `ANTPATH_DEV_STACK_REQUIRE_AUTH_PROVIDER=1` is set.
|
|
367
408
|
|
|
368
409
|
Required runtime config includes:
|
|
369
410
|
|
|
@@ -374,7 +415,10 @@ Required runtime config includes:
|
|
|
374
415
|
- Auth.js secret/providers;
|
|
375
416
|
- SDK token hashing secret/pepper;
|
|
376
417
|
- worker identity;
|
|
377
|
-
- provider API base/version settings
|
|
418
|
+
- provider API base/version settings;
|
|
419
|
+
- dashboard BFF rate-limit defaults;
|
|
420
|
+
- worker polling/lease/metrics settings.
|
|
421
|
+
- dev stack ports for dashboard and worker.
|
|
378
422
|
|
|
379
423
|
Environment-configurable defaults include:
|
|
380
424
|
|
|
@@ -396,26 +440,28 @@ Environment-configurable defaults include:
|
|
|
396
440
|
- workspace storage cap;
|
|
397
441
|
- signed URL TTL;
|
|
398
442
|
- free user allowance;
|
|
399
|
-
- metadata retention toggles
|
|
443
|
+
- metadata retention toggles;
|
|
444
|
+
- audit/rate-limit windows for sensitive BFF actions.
|
|
445
|
+
- live e2e opt-in flags for provider sessions, public MCP endpoints, Anthropic skill IDs, and timeouts.
|
|
400
446
|
|
|
401
447
|
Missing optional env vars must fall back to conservative low limits. Missing required secret/connectivity env vars must fail service startup.
|
|
402
448
|
|
|
449
|
+
Live public MCP/skills e2e tests are intentionally opt-in because they depend on external provider and public MCP availability. Deterministic unit/component tests validate request wiring for MCP server declarations, tool policies, skill references, and secret non-leakage; the live e2e validates the real provider accepts and runs that configuration end to end.
|
|
450
|
+
|
|
451
|
+
No-cost provider resilience and load testing uses sanitized replay snapshots from real Managed Agents sessions. Replay tests inject network disconnects, transient HTTP failures, provider latency, slow SSE chunks, cleanup failures, and concurrent full-session load around those snapshots. Idempotent read/delete-style calls may retry with bounded backoff; provider create/send POST calls are not retried by default because their side effects may already have happened.
|
|
452
|
+
|
|
403
453
|
## Open implementation details
|
|
404
454
|
|
|
405
455
|
These are not architecture blockers, but must be pinned during implementation planning:
|
|
406
456
|
|
|
407
|
-
-
|
|
408
|
-
- Exact Auth.js providers.
|
|
409
|
-
- Auth.js adapter/session mode and user mirror lifecycle.
|
|
410
|
-
- Supabase Vault access method and SQL privilege wrapper shape.
|
|
411
|
-
- Signed URL TTL and whether storage paths include random unguessable components in addition to workspace/run ids.
|
|
412
|
-
- Deletion audit requirements.
|
|
457
|
+
- Production plan tier values for duration, concurrency, storage, polling, and free user allowance.
|
|
413
458
|
- Provider metadata naming convention.
|
|
414
459
|
- Provider list/search capabilities and reconciliation coverage for each resource type.
|
|
415
460
|
- Exact Claude Managed Agents event pagination/filter semantics and bounded polling fallback.
|
|
416
461
|
|
|
417
462
|
## Backlog
|
|
418
463
|
|
|
464
|
+
- Persistent workspace-level provider connections (BYO Anthropic key custody across runs, with rotation, revocation, and re-use). The MVP submission contract carries the key inline per run instead.
|
|
419
465
|
- Provider webhooks as wakeup/reconciliation accelerator.
|
|
420
466
|
- SSE live event stream for richer UI.
|
|
421
467
|
- Supabase Realtime with explicit Auth.js-to-Supabase authorization design.
|
|
@@ -8,7 +8,7 @@ scope: platform MVP
|
|
|
8
8
|
|
|
9
9
|
## Goal
|
|
10
10
|
|
|
11
|
-
Convert antpath from an SDK-only package into a TypeScript workspace containing:
|
|
11
|
+
Convert antpath from an SDK-only package into a pnpm TypeScript workspace containing:
|
|
12
12
|
|
|
13
13
|
- a platform SDK in `packages/sdk`;
|
|
14
14
|
- a dashboard app in `apps/dashboard`;
|
|
@@ -21,27 +21,30 @@ Implementation is test-driven. Each behavior starts with the narrowest failing t
|
|
|
21
21
|
|
|
22
22
|
## Acceptance criteria
|
|
23
23
|
|
|
24
|
-
- The repository is
|
|
24
|
+
- The repository is a pnpm workspace and the current SDK is moved to `packages/sdk`.
|
|
25
25
|
- Dashboard and worker app folders exist with build/test integration.
|
|
26
26
|
- Auth.js authenticates dashboard users.
|
|
27
27
|
- SDK API tokens authenticate programmatic clients.
|
|
28
28
|
- Workspaces are the tenant boundary.
|
|
29
29
|
- BFF/server actions scope every dashboard/API operation by workspace membership or API-token scope.
|
|
30
30
|
- Supabase service-role credentials are never exposed to browser/client bundles.
|
|
31
|
-
- Supabase Postgres stores durable run, attempt, provider resource, event, output, cleanup, usage, workspace, membership
|
|
32
|
-
-
|
|
33
|
-
- Run submission idempotency is enforced by workspace and request hash.
|
|
31
|
+
- Supabase Postgres stores durable run, attempt, provider resource, event, output, output-capture-failure, cleanup, usage, workspace, and membership metadata. There is no persistent `provider_connections` table in MVP.
|
|
32
|
+
- Per-run secrets bundles (Anthropic key, optional MCP credentials, optional skill references) arrive inline on submission, are encrypted through Supabase Vault for the lifetime of that single run, and are deleted at terminal cleanup. The Vault entry is the only durable trace of the user's provider key.
|
|
33
|
+
- Run submission idempotency is enforced by workspace and request hash. The hash excludes the `secrets` block so re-submitting the same logical run with a new key still matches.
|
|
34
34
|
- Workers claim due runs with leases and `FOR UPDATE SKIP LOCKED`.
|
|
35
35
|
- Multiple workers can run concurrently without duplicate lifecycle ownership.
|
|
36
36
|
- Worker polling recovers from missed `NOTIFY`, worker restart, deploy, and expired leases.
|
|
37
37
|
- Worker creates per-run provider resources and journals them for cleanup.
|
|
38
38
|
- Worker polls provider status and events and stores only redacted metadata.
|
|
39
|
-
- Output capture
|
|
39
|
+
- Output capture is unconditional: every artifact Claude exposes on the run's session is written to private Supabase Storage, bounded only by the workspace storage cap. Files that would exceed the cap are persisted as `output_capture_failures` rows.
|
|
40
40
|
- BFF returns signed output links only after workspace authorization.
|
|
41
|
-
- Cleanup runs after terminal provider state and output capture.
|
|
41
|
+
- Cleanup runs after terminal provider state and output capture, deletes the per-run Vault entry, and (by default) deletes Claude-side resources. `cleanup.claudeSession: "retain"` only affects Claude-side resources.
|
|
42
42
|
- Reconciliation can recover intended provider resources after partial worker crashes.
|
|
43
43
|
- User deletion is pending/soft until cleanup and storage deletion are complete.
|
|
44
44
|
- Exact tier/cap values are configurable through environment variables with conservative defaults and run-level snapshots.
|
|
45
|
+
- Security-sensitive BFF/API actions are audited and rate-limited.
|
|
46
|
+
- Workspace storage quota and workspace deletion are enforced before provider/storage side effects proceed.
|
|
47
|
+
- CI runs pnpm lint/test/build/package checks plus a local Supabase integration job.
|
|
45
48
|
- Tests follow the accepted taxonomy: deterministic unit tests (including fakes and sanitized recorded snapshots), live external-system integration tests with no skip flags, and top-to-bottom live e2e tests.
|
|
46
49
|
|
|
47
50
|
## Phase 1: Workspace foundation
|
|
@@ -50,16 +53,16 @@ Create the workspace structure without changing runtime behavior.
|
|
|
50
53
|
|
|
51
54
|
Deliverables:
|
|
52
55
|
|
|
53
|
-
- Root
|
|
56
|
+
- Root pnpm workspace configuration.
|
|
54
57
|
- Current SDK moved to `packages/sdk`.
|
|
55
58
|
- `apps/dashboard` placeholder.
|
|
56
59
|
- `apps/worker` placeholder.
|
|
57
60
|
- Shared TypeScript config/build/test commands.
|
|
58
61
|
- Existing SDK exports preserved or intentionally migrated with compatibility notes.
|
|
59
62
|
- Root validation commands:
|
|
60
|
-
- `
|
|
61
|
-
- `
|
|
62
|
-
- `
|
|
63
|
+
- `pnpm lint`
|
|
64
|
+
- `pnpm test`
|
|
65
|
+
- `pnpm build`
|
|
63
66
|
|
|
64
67
|
TDD gate:
|
|
65
68
|
|
|
@@ -113,23 +116,25 @@ Deliverables:
|
|
|
113
116
|
|
|
114
117
|
- Migration framework.
|
|
115
118
|
- Tables:
|
|
116
|
-
- `users`;
|
|
119
|
+
- Auth.js adapter tables: `users`, `accounts`, `sessions`, `verification_token`;
|
|
120
|
+
- antpath `app_users`;
|
|
117
121
|
- `workspaces`;
|
|
118
122
|
- `workspace_memberships`;
|
|
119
123
|
- `api_tokens`;
|
|
120
|
-
- `
|
|
121
|
-
- `runs`;
|
|
124
|
+
- `runs` (includes `execution_secret_id` referencing the per-run Vault entry);
|
|
122
125
|
- `run_attempts`;
|
|
123
126
|
- `provider_resources`;
|
|
124
127
|
- `run_events`;
|
|
125
128
|
- `output_objects`;
|
|
129
|
+
- `output_capture_failures`;
|
|
126
130
|
- `cleanup_attempts`;
|
|
127
131
|
- `usage_ledger`.
|
|
128
132
|
- Constraints:
|
|
129
133
|
- unique `(workspace_id, idempotency_key)` on runs;
|
|
130
134
|
- request-hash conflict handling;
|
|
131
135
|
- unique `(run_attempt_id, provider_event_id)` on events;
|
|
132
|
-
- foreign keys for workspace and attributed
|
|
136
|
+
- foreign keys for workspace and attributed `app_user_id` where practical;
|
|
137
|
+
- RLS enabled and `anon`/`authenticated` direct table access revoked for platform/Auth.js tables.
|
|
133
138
|
- DB query helpers for tenant-scoped access.
|
|
134
139
|
|
|
135
140
|
TDD gate:
|
|
@@ -174,10 +179,11 @@ Turn the SDK into the programmatic client for the platform while preserving Temp
|
|
|
174
179
|
Deliverables:
|
|
175
180
|
|
|
176
181
|
- SDK client for platform API base URL and API token.
|
|
177
|
-
-
|
|
182
|
+
- `AntpathPlatformClient` constructor accepts a default `secrets` bundle (Anthropic key + optional MCP credentials + optional skill references); every method accepts a per-call override.
|
|
183
|
+
- Submit run API (carries inline `secrets`).
|
|
178
184
|
- Get run status/detail API.
|
|
179
185
|
- List metadata events API.
|
|
180
|
-
- List outputs API.
|
|
186
|
+
- List outputs API (returns successful captures and capture failures).
|
|
181
187
|
- Create signed output link API.
|
|
182
188
|
- Cancel run API.
|
|
183
189
|
- Delete run API.
|
|
@@ -187,10 +193,11 @@ Deliverables:
|
|
|
187
193
|
TDD gate:
|
|
188
194
|
|
|
189
195
|
- Type/contract tests for public SDK API and runtime tests with fake platform responses.
|
|
196
|
+
- Tests asserting `secrets` never appears in serialized request hashes, error metadata, or retry telemetry.
|
|
190
197
|
|
|
191
198
|
Validation:
|
|
192
199
|
|
|
193
|
-
- SDK
|
|
200
|
+
- SDK does not persist provider keys: it forwards them to the platform per submission, where they are vaulted for the lifetime of one run and then deleted.
|
|
194
201
|
- SDK handles idempotency conflict, unauthorized, quota, and terminal states deterministically.
|
|
195
202
|
|
|
196
203
|
## Phase 6: Worker claim loop and state machine
|
|
@@ -203,7 +210,7 @@ Deliverables:
|
|
|
203
210
|
- Optional Postgres `NOTIFY` listener for fast wakeup.
|
|
204
211
|
- Lease claim/release helpers.
|
|
205
212
|
- Lease-guarded status update helper.
|
|
206
|
-
- Per-workspace
|
|
213
|
+
- Per-workspace rate limit hooks.
|
|
207
214
|
- Fair due-run ordering across workspaces.
|
|
208
215
|
- Cancellation/delete request checks before side effects.
|
|
209
216
|
- Timeout handling.
|
|
@@ -295,58 +302,65 @@ Validation:
|
|
|
295
302
|
|
|
296
303
|
## Phase 10: Output capture and Supabase Storage
|
|
297
304
|
|
|
298
|
-
|
|
305
|
+
Unconditionally capture every artifact the user's Claude session exposes into private Supabase Storage. The workspace storage cap is the only user-visible quota.
|
|
299
306
|
|
|
300
307
|
Deliverables:
|
|
301
308
|
|
|
302
|
-
-
|
|
303
|
-
-
|
|
304
|
-
-
|
|
305
|
-
- Streaming download with
|
|
306
|
-
- Private Supabase Storage upload.
|
|
307
|
-
-
|
|
308
|
-
-
|
|
309
|
-
-
|
|
310
|
-
- Per-user attribution from run row.
|
|
311
|
-
- Signed-link BFF action/API.
|
|
309
|
+
- Worker terminal-state list of session-scoped files via the Claude Files API (`GET /v1/files?scope_id=<sessionId>`), returning `id`, `filename`, and `size_bytes`.
|
|
310
|
+
- Workspace storage usage accounting helper.
|
|
311
|
+
- Pre-download quota check: if `(workspace_used + size_bytes) > workspace cap`, write an `output_capture_failures` row with `reason = "workspace_quota_exceeded"` and continue without downloading.
|
|
312
|
+
- Streaming download via `GET /v1/files/{file_id}/content` with a worker-internal safety cap so malformed listing responses cannot OOM the worker; safety-cap aborts are recorded as `output_capture_failures` with `reason = "download_failed"`.
|
|
313
|
+
- Private Supabase Storage upload with workspace-scoped path policy.
|
|
314
|
+
- `output_objects` metadata insert for each successful capture.
|
|
315
|
+
- Per-user attribution frozen from the run row (audit/billing dimension only; not a quota).
|
|
316
|
+
- Signed-link BFF action/API for both `output_objects` rows; capture failures are read-only.
|
|
312
317
|
|
|
313
318
|
TDD gate:
|
|
314
319
|
|
|
315
|
-
- Add failing tests for quota
|
|
320
|
+
- Add failing tests for: pre-download quota check choosing failure-row over download when over cap; safety-cap abort recording a failure row; happy path writing both an `output_objects` row and an audit entry; signed-link authorization; cross-workspace denial; listing returning zero files producing zero rows; listing returning N files with mixed sizes recording the expected mix of successes and failures.
|
|
321
|
+
- Tests must assert there is no user-facing knob for `capture: boolean`, `globs`, or per-file caps.
|
|
316
322
|
|
|
317
323
|
Validation:
|
|
318
324
|
|
|
319
|
-
-
|
|
325
|
+
- Output capture is unconditional and cannot be opted out by the submitter.
|
|
326
|
+
- Oversized output payloads do not OOM the worker.
|
|
320
327
|
- BFF only creates signed links for authorized workspace users/tokens.
|
|
328
|
+
- Dashboard renders successful captures and capture failures side by side.
|
|
321
329
|
|
|
322
330
|
## Phase 11: Cleanup, deletion, and retention
|
|
323
331
|
|
|
324
|
-
Make cleanup and deletion first-class state machines.
|
|
332
|
+
Make cleanup and deletion first-class state machines. Cleanup must also destroy the per-run Vault secret.
|
|
325
333
|
|
|
326
334
|
Deliverables:
|
|
327
335
|
|
|
328
|
-
- Cleanup ordering:
|
|
329
|
-
-
|
|
330
|
-
- session files/session where supported;
|
|
331
|
-
- agent/archive;
|
|
332
|
-
- environment/archive
|
|
333
|
-
- uploaded provider
|
|
334
|
-
-
|
|
335
|
-
-
|
|
336
|
+
- Cleanup ordering (per the architecture decisions doc):
|
|
337
|
+
- capture session-scoped files (Phase 10);
|
|
338
|
+
- provider session files / session where supported;
|
|
339
|
+
- agent / archive;
|
|
340
|
+
- environment / archive or delete;
|
|
341
|
+
- skills uploaded for this session and other ephemeral provider resources;
|
|
342
|
+
- provider Vault/credentials created on Claude's side for MCP wiring;
|
|
343
|
+
- the antpath per-run Vault entry: `vault.deleteSecret(runs.execution_secret_id)` and clear the column;
|
|
344
|
+
- local Supabase Storage and metadata only on user deletion.
|
|
345
|
+
- The Claude-side cleanup steps are skipped when `cleanup.claudeSession === "retain"` (or the worker default is `retain`); the per-run Vault deletion still runs.
|
|
346
|
+
- Cleanup retry/backoff with idempotent calls.
|
|
336
347
|
- `cleanup_attempts` records.
|
|
337
348
|
- Cleanup state separate from user-facing run terminal state.
|
|
338
349
|
- User `pending_delete` flow.
|
|
339
350
|
- Workspace deletion flow.
|
|
340
|
-
-
|
|
351
|
+
- Audit logs and rate limits for delete/cancel/signed-link/API-token mutations.
|
|
341
352
|
|
|
342
353
|
TDD gate:
|
|
343
354
|
|
|
344
|
-
- Add tests for cleanup after success, failure, timeout, cancellation, partial provider creation, duplicate cleanup calls,
|
|
355
|
+
- Add tests for cleanup after success, failure, timeout, cancellation, partial provider creation, duplicate cleanup calls, and pending-delete races.
|
|
356
|
+
- Add tests that the per-run Vault entry is deleted exactly once and `runs.execution_secret_id` is cleared whether or not Claude-side resources are retained.
|
|
357
|
+
- Add tests that a key rotated on the provider side mid-run produces a `tenant_permanent` failure on the next provider call and that cleanup still runs (and the Vault entry is still deleted).
|
|
345
358
|
|
|
346
359
|
Validation:
|
|
347
360
|
|
|
348
361
|
- Cleanup failures surface actionable redacted errors.
|
|
349
362
|
- Hard deletion only happens after cleanup/storage deletion succeeds.
|
|
363
|
+
- The per-run Vault entry is always destroyed at terminal cleanup, regardless of retention knobs.
|
|
350
364
|
|
|
351
365
|
## Phase 12: Minimal dashboard
|
|
352
366
|
|
|
@@ -407,18 +421,26 @@ Deliverables:
|
|
|
407
421
|
- Cleanup in `finally`.
|
|
408
422
|
- Release/readiness docs.
|
|
409
423
|
- Updated README and examples for platform SDK usage.
|
|
424
|
+
- GitHub Actions pnpm CI and local Supabase integration job.
|
|
425
|
+
- Vercel dashboard and Railway worker environment-variable contracts.
|
|
426
|
+
- `pnpm dev:stack` for local Supabase/dashboard/worker startup with fail-fast config checks.
|
|
427
|
+
- Deterministic MCP/skills request-wiring tests for SDK and worker providers.
|
|
428
|
+
- Separately gated public MCP plus Anthropic skill full-session e2e.
|
|
410
429
|
|
|
411
430
|
TDD gate:
|
|
412
431
|
|
|
413
432
|
- Live e2e is not a TDD driver for core logic, but must prove final integration before release.
|
|
433
|
+
- MCP/skills request shape, permission policy, and secret non-leakage must be covered by normal deterministic tests before relying on public live e2e.
|
|
414
434
|
|
|
415
435
|
Validation:
|
|
416
436
|
|
|
417
437
|
- Full submit -> provider session -> metadata poll -> output capture -> signed link -> cleanup works.
|
|
418
|
-
-
|
|
438
|
+
- Public MCP/skills e2e runs through the explicit `pnpm test:e2e:live` command with live credentials, reaches success, emits SDK/provider events, records safe provider IDs, exercises the default cleanup path, and exercises the `cleanup.claudeSession = "retain"` override path with a follow-up provider API check.
|
|
439
|
+
- Default `pnpm lint`, `pnpm test`, and `pnpm build` pass.
|
|
419
440
|
|
|
420
441
|
## Backlog
|
|
421
442
|
|
|
443
|
+
- Persistent workspace-level provider connections (saved Anthropic keys with rotation/revocation/re-use). The MVP submission contract carries the key inline per run.
|
|
422
444
|
- Provider webhooks as wakeup/reconciliation accelerator.
|
|
423
445
|
- SSE live event stream for richer dashboard UI.
|
|
424
446
|
- Supabase Realtime with explicit Auth.js-to-Supabase authorization design.
|