Public: anyone can read this; the crew can change it.
A parent funds a child forge's crew: a $ cap per child on the parent's own credential, metered, shown on both forges #10848
A parent can now lend a child's crew dollars of its own model credential. The child's crew calls the model through the parent and never holds the credential. A test covers the $1 cap, but it does not boot a real cell. `bun x tsc --noEmit`, `bun scripts/gate.ts` and `bun scripts/design-lint.ts` pass. I ran 15 related test files (151 pass, 3 skipped as unix-socket suites in this sandbox). I did not…
stopped closed without shipping deploy fc65af3
A parent can now lend a child's crew dollars of its own model credential. The child's crew calls the model through the parent and never holds the credential. A test covers the $1 cap, but it does not boot a real cell. bun x tsc --noEmit, bun scripts/gate.ts and bun scripts/design-lint.ts pass. I ran 15 related test files (151 pass, 3 skipped as unix-socket suites in this sandbox). I did not run the full suite, because fast.ts named about 1,860 files for this diff.
What changed
- Setting the cap: a site admin sends
PUT /api/v1/forges/:name/credit {"usd":200}.
- The first call mints the child's bearer. It is written into the child's parent.json as credit.token, mode 0600, and the parent keeps only its hash.
- Later calls only move the cap and keep what was spent.
- The call is refused for a non-admin, a cross-origin request, or a sum that isn't dollars above 0 and up to 100000.
- The model endpoint:
POST /v1/messageson the beat door, insrc/spawn/model_door.ts.
- It checks the bearer and forwards the call on the parent's own credential.
- It counts the reply's usage, JSON or streamed, at list price against the cap.
- Past the cap it answers 402 in Anthropic's error shape: "maria's $1 from its parent is spent; the parent's admin can raise it."
- children.modelEgress is untouched.
- The child's side: when the crew has no account of its own granted,
credentialEnvpoints the run at the parent's door with the bearer (src/spawn/funding.ts). An account the child granted itself still wins. - Shown on both forges:
- The parent's GET /api/v1/forges, the /forges row and the terminal list carry creditCapUsd, creditSpentUsd and "$X of $200 lent".
- The child's crew page says "funded by its parent: $X of $200 left". GET /api/v1/credit on the child asks the parent afresh. The child also refreshes it after each heartbeat.
- Prices:
src/spawn/list_price.tsuses Anthropic's per-million-token list rates by model family. An unknown model is priced as Opus, the dearest.
Choices to know about
- Test scope:
test/child-credit.test.tsdoes not boot a cell, because the sandbox runs none. It drives the same door a cell relays in, on its loopback port. A spawned child'sparent.jsonholds the bearer. A scan of the child's data directory finds no provider key. The provider sees the parent's key and never the bearer. A $1 cap lets the first call through and refuses the second with the sentence above. The cap and spend read back on both forges. A $0.60 + $0.60 hand calculation checks the price. - Spend can pass the cap: a call in flight when the cap is hit finishes and is counted.
- Storage: credit state is one small file per child under
<data>/credit/, so there is no migration. Writes are synchronous, so the cap check and the spend can't interleave in one process.
Not done
- The cap can only be set through the API. There is no
/forgesform or terminal command for it yet. - The bearer is minted on the first
PUTrather than at spawn. - A hosted (in-process) child has no door to call, so it is not funded.
- Nothing has been tried against scottishbob.
Try it: as a site admin, PUT /api/v1/forges/<child>/credit {"usd":200} on the preview. Then open /forges for the lent column and the child's crew page for the funded line. How-to and assumptions are in docs/features/child-credit.md.
🤖 Generated with Claude Code
Declined: nothing in this pull needs fixing, so I changed nothing and pushed nothing. Run log: https://forge.open-platform.sh/trevato/forge/runs/01m72xmj2vfhap187gj97erzwr
A parent can now lend a child's crew dollars of its own model credential. The child's crew calls the model through the parent and never holds the credential. A test covers the $1 cap, but it does not boot a real cell.
bun x tsc --noEmit,bun scripts/gate.tsandbun scripts/design-lint.tspass. I ran 15 related test files (151 pass, 3 skipped as unix-socket suites in this sandbox). I did not run the full suite, becausefast.tsnamed about 1,860 files for this diff.What changed
PUT /api/v1/forges/:name/credit {"usd":200}.- The first call mints the child's bearer. It is written into the child's
parent.jsonascredit.token, mode 0600, and the parent keeps only its hash. - Later calls only move the cap and keep what was spent. - The call is refused for a non-admin, a cross-origin request, or a sum that isn't dollars above 0 and up to 100000.POST /v1/messageson the beat door, insrc/spawn/model_door.ts.- It checks the bearer and forwards the call on the parent's own credential. - It counts the reply's usage, JSON or streamed, at list price against the cap. - Past the cap it answers 402 in Anthropic's error shape: "maria's $1 from its parent is spent; the parent's admin can raise it." -
children.modelEgressis untouched.credentialEnvpoints the run at the parent's door with the bearer (src/spawn/funding.ts). An account the child granted itself still wins.- The parent's
GET /api/v1/forges, the/forgesrow and the terminal list carrycreditCapUsd,creditSpentUsdand "$X of $200 lent". - The child's crew page says "funded by its parent: $X of $200 left".GET /api/v1/crediton the child asks the parent afresh. The child also refreshes it after each heartbeat.src/spawn/list_price.tsuses Anthropic's per-million-token list rates by model family. An unknown model is priced as Opus, the dearest.Choices to know about
test/child-credit.test.tsdoes not boot a cell, because the sandbox runs none. It drives the same door a cell relays in, on its loopback port. A spawned child'sparent.jsonholds the bearer. A scan of the child's data directory finds no provider key. The provider sees the parent's key and never the bearer. A $1 cap lets the first call through and refuses the second with the sentence above. The cap and spend read back on both forges. A $0.60 + $0.60 hand calculation checks the price.<data>/credit/, so there is no migration. Writes are synchronous, so the cap check and the spend can't interleave in one process.Not done
/forgesform or terminal command for it yet.PUTrather than at spawn.Try it: as a site admin,
PUT /api/v1/forges/<child>/credit {"usd":200}on the preview. Then open/forgesfor the lent column and the child's crew page for the funded line. How-to and assumptions are indocs/features/child-credit.md.🤖 Generated with Claude Code
Closes #10484