Skip to content

queen: z.ai lease route for the Mac reviewer, decided by key_vault.t27 - #2047

Open
gHashTag wants to merge 1 commit into
queenfrom
queen-key-vault-lease
Open

gHashTag wants to merge 1 commit into
queenfrom
queen-key-vault-lease

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Why

The owner's rule of 2026-10-18 has two parts. Every API key lives in one store and is shared out among everyone who uses it. The Mac reviewer (tools/bees/reviewer.py in gHashTag/t27) stops running on five private z.ai keys that the Queen cannot see.

gHashTag/t27#10567 adds the rule as a card, specs/queen/key_vault.t27, and points the reviewer at BEE_QUEEN_LEASE_URL / BEE_QUEEN_LEASE_TOKEN. This PR is the route behind that URL.

What

File Change
specs/queen/key_vault.t27, key_failover.t27, key_vault.wasm, services/queen-key-vault-card.gen.ts Vendored from gHashTag/t27#10567 (head ae8bcbe6f). The blobs, the build command, the 11 exports and the sha256 of each file are in specs/PIN. The wasm imports nothing, and two builds gave one hash.
routes/queen-lease.ts (+40/-1) createQueenZaiLeaseRoute(). It answers 503 until TRIOS_QUEEN_REVIEW_LEASE_TOKEN (at least 32 bytes) is set, and 403 to any other bearer (sha256 digests compared with timingSafeEqual). The card's lease_answer decides grant, wait or refuse: the free lanes are the z.ai review lanes that no bee (runningKeys), backoff or lease holds, a review counts as waiting, and total includes the lanes the bees hold. A wait is a 429 whose retry-after comes from retry_after_seconds. The route forwards only content-type and anthropic-version: never the caller's token.
services/queen-free-lane.ts (+40) vault() is the card's wasm. leased is the keys out on a lease now. zaiLeaseCall() makes the request on https://api.z.ai/api/anthropic with one lane's key and streams the answer back; the lease ends when the stream ends, or after LEASE_SECONDS. When a lane refuses, the card's lease_step hands the request to the next lane, at most LEASE_TRIES lanes, and a wrong request (KE_REQUEST) goes back unretried.
services/queen-reviewer.ts (+10) zaiLeaseLanes(taken, busy): the z.ai review lanes no bee, backoff or lease holds.
server.ts Mounts /queen/zai-lease as a server-to-server route, next to /queen/contributor-keys.
tools/route-guard-audit.mjs, tests/api/routes/route-guard.test.ts An allowlist entry with its reason. Pins: 66 to 67 mounts, 41 to 42 /queen mounts, 10 to 11 allowlisted.

No new hand-written file. Every lease decision is the card's; the TypeScript is I/O around it.

Known imprecision

  • A lease is counted per lane key (reviewerLaneKey), so the two concurrent requests a z.ai key carries count as one. That is conservative: the route lends fewer requests than z.ai allows, never more.
  • The route serves one consumer kind (LEASE_REVIEW). Bee-side leasing through the card is a follow-up.

Owner steps after merge

  1. Set TRIOS_QUEEN_REVIEW_LEASE_TOKEN (at least 32 random bytes) on trios-agent-server, with skipDeploys: true, and redeploy the queen build.
  2. Put BEE_QUEEN_LEASE_URL=https://<agent-server host>/queen/zai-lease and BEE_QUEEN_LEASE_TOKEN=<the same token> in ~/.claude/.env on the Mac.
  3. Paste the five Mac z.ai keys into the store through the contributor-key flow, then delete the ZAI_KEY_* lines. Once the lease is set, the reviewer refuses to start while they remain.

Check

Biome and the route-guard suite run in this PR's CI. Nothing was built or run on the owner's Mac.

πŸ€– Generated with Claude Code

The owner's rule (2026-10-18): every API key lives in one store and is
shared out among its users; the Mac reviewer stops running on private
z.ai keys. This mounts /queen/zai-lease: the Mac reviewer's claude CLI
points ANTHROPIC_BASE_URL at it, and the Queen makes the request on one
of her own z.ai lanes, so no key value leaves her.

- specs/queen/key_vault.t27, key_failover.t27, key_vault.wasm and
  queen-key-vault-card.gen.ts vendored from gHashTag/t27#10567 (hashes
  and build command in specs/PIN).
- lease_answer decides grant / wait / refuse, retry_after_seconds the
  wait, lease_step whether a refused lane hands the request to the next
  (at most LEASE_TRIES lanes, a wrong request never retried).
- The route answers 503 until TRIOS_QUEEN_REVIEW_LEASE_TOKEN (>= 32
  bytes) is set, 403 to any other bearer; the caller's token is never
  forwarded and a key is never returned.
- route-guard allowlist entry with its reason; pins re-measured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tri-claw

tri-claw Bot commented Oct 18, 2026

Copy link
Copy Markdown

Summary by t27-bees

Checked by the t27 compiler

t27c parse, parse-complete and typecheck on each changed .t27 file at 0293ce7. Nothing was generated, built or run. A pass means the file is well formed, not that it is right.

  • trios/agent-server/specs/queen/key_failover.t27: parses and typechecks.
  • trios/agent-server/specs/queen/key_vault.t27: parses and typechecks.

Read by the model

This PR adds a /queen/zai-lease route so the Mac reviewer can lease z.ai keys from the Queen's key vault instead of using its own private keys. The route authenticates with a shared token, asks the key_vault.t27 Wasm card for a lease decision, and if granted, forwards the request to z.ai on a free lane key.

Worth a look

  • trios/agent-server/apps/server/src/api/routes/queen-lease.ts (lines 195–215): The ask array passes n = leased.size (total leased keys across all consumers) as both held (lanes this asker holds) and review_holding (reviews holding lanes). For a single Mac reviewer this conflates global lease count with per-holder lane count; the spec expects lane counts (where each key = 2 lanes). This unit mismatch makes lease_answer compute with wrong magnitudes β€” could grant or wait incorrectly.

  • trios/agent-server/apps/server/src/api/routes/queen-lease.ts (line 200): total = zaiLeaseLanes([], leased).length + leased.size adds free keys + leased keys, but the spec's total parameter is total lanes. Since each key carries 2 lanes (ZAI_REQUESTS_PER_KEY = 2), this undercounts total capacity by leased.size lanes. The known imprecision calls this "conservative," but combined with the held/review_holding mismatch above, the net effect on lease_answer is unverified.

  • trios/agent-server/apps/server/src/api/services/queen-free-lane.ts (lines 105–108): The zai_code is extracted via regex /"code"\s*:\s*"?(\d+)/ from the response text. If z.ai returns non-JSON or a different field name, code defaults to 0, which error_kind classifies as KE_TRANSIENT for 429 status (correct) but KE_REQUEST for 400/404 (would incorrectly retry a bad request on another lane). The Wasm's lease_step treats KE_REQUEST as LS_ANSWER (no retry), so a misclassified 400/404 would not retry β€” but a misclassified 429 with no code would retry (transient), which is probably safe.

  • trios/agent-server/apps/server/src/api/services/queen-free-lane.ts (line 92): leased is a Map<string, symbol> keyed by reviewerLaneKey(lane) (the key string). Two concurrent requests on the same z.ai key share one map entry. The free() cleanup checks leased.get(key) === id before deleting, so the last request to finish releases the key β€” correct. However, setTimeout(free, 600_000) and AbortSignal.timeout(600_000) race: if the fetch aborts at 600s, free() runs immediately and again at 600s. The symbol check makes the second call a no-op, so no leak.

  • trios/agent-server/specs/queen/key_vault.t27 (line 212): lease_answer treats free_lanes == 0 as LA_WAIT before checking holder caps. The route passes lanes.length (free keys) as free_lanes. If 1 free key exists (2 lanes), free_lanes=1 passes the check, but the spec's holder_cap logic then compares held (keys) against cap (lanes). With REVIEW_FLOOR_LANES=2 and REVIEW_HOLDERS=3, holder_cap(2, 3)=1 lane. A reviewer with held=0 keys gets 0 < 1 β†’ LA_GRANT. But if held=1 key (2 lanes), 1 < 1 is false, then free_lanes=1 > cap=1 is false β†’ LA_WAIT, even though 2 lanes are free. This effectively limits each holder to 1 key (2 lanes) even when more are free β€” may be intentional but the unit confusion makes it hard to verify.


Read: 11 files, +920 -6, the whole diff. Head 0293ce7. Model: openai-compatible/nvidia/nemotron-3-ultra-550b-a55b. Commands: @t27-bees review | summary | help | pause | resume. Free for every repository.

@github-actions

Copy link
Copy Markdown

βœ… Tests passed β€” 3915/3988

Suite Passed Failed Skipped
βœ… agent 429/429 0 0
βœ… build 9/9 0 0
βœ… cdp-protocol 5/5 0 0
βœ… eval 93/93 0 0
βœ… server-agent 280/280 0 0
βœ… server-api 2273/2345 0 72
βœ… server-browser 6/6 0 0
βœ… server-integration 10/11 0 1
βœ… server-lib 279/279 0 0
βœ… server-pglive 166/166 0 0
βœ… server-root 68/68 0 0
βœ… server-sim 8/8 0 0
βœ… server-skills 31/31 0 0
βœ… server-tools 244/244 0 0
βœ… shared 14/14 0 0

View workflow run

@dmitrii-f-t27

Copy link
Copy Markdown
Collaborator

Status: blocked on t27#10567 (owner merge)

This trios issue wires specs/queen/key_vault.t27 into the live Queen dispatcher. The spec itself is in gHashTag/t27 PR #10567 (open, mergeable=true), but I have pull-only access to gHashTag/t27 β€” no push, no merge. The owner (gHashTag) must merge #10567 first.

CI on #10567: one pre-existing failure (t27b-native-ratchet, Node.js 20 deprecation in .github/ CI config β€” documented in t27#10567 comments), unrelated to key_vault.t27. The spec file passes all local t27c checks.

What I can do: nothing further on the trios side until #10567 merges into gHashTag/t27 master. Once merged, key_vault.t27 exists on master and the trios-side wiring PR can be written.

Check runs on #10567: https://github.com/gHashTag/t27/pulls/10567

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants