Source-candidate: poe d9a564e6d20f5f0fd75ce45b12cbbf8bb4c9d568\nSource-candidate: magica38ba5762b06716db1dee00d0afec162656488f5c\nValidated321whole-file phase plus whole bus1349/model-proxy100/webui490; final gate tooling checks pending, not landed.
28 KiB
Design: CLIProxyAPI as the stack's model credential and routing layer
Written 2026-10-24 by Sage for Mos's mos-1597 (Vikunja 237). It carries out Jason's Q249 ruling that CLIProxyAPI
becomes the stack's credential and routing layer, with Q116 and Q127 still in force. This is a design document.
Mos rules it before any code. Its build rows go to new Codex gpt-6.1-sol threads (Q246), and each gets its own
brief once Mos has ruled. Line numbers are at origin/refactor 2ce476d54.
Amendments. Mos's mos-1600, mos-1601 and mos-1602 changes live in the row briefs
(docs/plans/2026-10-24_model-proxy-rows.md), which win over this design where they differ. Jason's Q46
and Q47 answers (DECISIONS Q250, mos-1606, 2026-10-24) are written into this file: accounts are entered in
the Console's own proxy page, and the /proxy/ forward to the upstream panel (D8) is dropped. The P4 brief
for that page is docs/plans/2026-10-24_proxy-page-brief.md. Text marked "(Q250)" below is that change.
Not to be confused with queue row 237 ("one decision lands once", 2026-10-20_agent-credentials.md). This is
Vikunja task 237.
Inputs
- Q116 (jason-master-restart DECISIONS.md:709, :724-725; lead-decisions.md:2578-2585). The WebUI goes under mosaic.woltje.com behind Authentik, on a path of the existing host. A new DNS record or subdomain goes back to Jason first. Authentik admits only jason.woltje. No agent reads the management key.
- Q127 (:779-782). Seats talk only to a local proxy, using keys the stack issues and rotates. Upstream tokens and keys stay in the proxy. Switching live seats to the proxy, and the subscription-terms check, go back to Jason.
- Q249 (:2512). The proxy serves one user's agents on that user's machine. It is not shared with other users and not used in a commercial app. The host proxy on 127.0.0.1:8317 is not touched. Decision 108's token file and claude-session.env are a stopgap. One proxy per user.
- Q153/Q154 (:1088, :1096): usage sources and quota enforcement sit behind a plug-in interface. Q222 (:2194): limits are settings with defaults, and a setting that guards the human from agents is human-only.
- Row 96 (#1590),
agents/researcher/work/i1-proxy-test/REPORT.md, tested against CLIProxyAPI 8.0.27. Issue is 1 PUT, revoke is 1 DELETE, with no restart and effect on the next request. Keys survive a restart. Claude Code works withANTHROPIC_BASE_URL+ANTHROPIC_AUTH_TOKEN. Pinned pi 0.85.1 needs amodels.jsonin the session HOME. Eight open points (:190-216) and six design items (:218-236). Row 96's queue note adds two more.GET api-keysreturns every client key in full. The claim that the 401 body shape causes the Claude Code stall is unverified, so test it before any fix aimed at the shape. - Row 199 (landed
cb5fdb064): the agent credential store, files under<configDir>/secrets/agents/<business>/<agent>/<kind>, 0700 directories and 0600 files (agents/megavolt/work/queue-199/DESIGN.md:63-65). ClassCredentialsis inpackages/bus/src/credentials.mjs(issue:291,revoke:383,remove:400,useFor:417,status:474). D7 (:118-125) deferred per-agent model tokens to "a proxy ... its own row". This is that row. - Row 194 (landed
fd6dd327c): fire stops the agent's runs, then delists it (packages/bus/src/broker.mjs#chiefLifecycle:5038,instance.fire:5117).revoke()inpackages/bus/src/runtime.mjs:166-197 revokes before the write and refusescredential-revoke-failed. - How sessions get model credentials today. The bus unit loads
claude-session.env(packages/cli/systemd/mosaic-bus.service.in:21-25).sessionEnvpasses everyENV_ALLOWvariable to every session (packages/seat/src/session.mjs:242-262). Inside the sandbox, HOME is the run's ownhome/(:240-241). The sandbox doesn't unshare the network (packages/seat/src/sandbox.mjs:70), so a session reaches host loopback. Pi has no model credential today (P19 #383). There is no Codex harness plug-in (packages/plugins/harness/holds claude-code and pi only).
1. Where the proxy runs
D1. One proxy per stack instance, run by the stack. It runs as the instance's OS user, and since every
instance has one user (invariant 2) that gives one proxy per user per machine. The default instance on
dragon-lin gets one. Melanie's .54 hub gets its own as mosaic under /srv/mosaic. Nothing is shared
between instances. The host proxy on 127.0.0.1:8317 is not started, stopped, read or configured by any of
this, and its config and data directory are not read.
D2. Process and files.
- A systemd user unit
mosaic-proxy.service, written by the same scripts that writemosaic-bus.service(scripts/instance.sh,scripts/case2-unit.sh). The bus unit getsWants=andAfter=on it. The bus refuses to launch a session when the proxy is configured but not answering (D17). - The proxy's own directory,
<dataRoot>/proxy/, mode 0700, holdsconfig.yaml(0600),auth/(theoauth.auth-dirfor its OAuth logins, 0700) andlogs/. No other component reads files in it. Run records never copy from it. - It listens on
127.0.0.1:<proxy.port>.allow-remote: false, so management calls are accepted from loopback only.
D3. System config. The instance config gets an optional proxy section: { "port": N, "binary": "<path>" }.
With no section, the instance runs as today on claude-session.env. That is how migration works one instance
at a time (section 9). With the section, both fields are required, and a missing or bad value refuses. The
port can't be 8317, and the bus refuses at boot if another process already listens on it. Bootstrap writes
the section only for a new config, as it does for every other field. An existing config gets it by a human
edit at cutover (Jason's step J2). This changes the config schema, so Mos must rule on it.
D4. Pinned binary. packages/model-proxy/PIN records the CLIProxyAPI version, the release asset name and
its sha256, the same way pi is pinned exactly. The tested version is 8.0.27. The unit starts only a binary
whose sha256 matches PIN. The control panel asset is pinned the same way, with
disable-auto-update-panel: true. The proxy otherwise downloads the panel from GitHub at runtime
(config.example.v8.0.27.yaml:94-103), and that would be unreviewed code behind Authentik. The stack never
copies the 8317 binary. The first fetch of the release asset counts as external access, so it is Jason's
step J1 or a row Mos approves. For containerized installs the binary and panel go into the proxy image
(section 8). Q250: nothing serves the panel to the Console now. P1 keeps the pin as built, and
removing it is a later cleanup.
D5. Initial config, then hands off. The stack writes config.yaml once, when <dataRoot>/proxy/ is
first created, from a template in packages/model-proxy/, and never writes the file again. After that the
stack changes the proxy only through the management API, and Jason changes it only through the Console's
proxy page, which calls the bus, which calls the management API (Q250).
Row 96 point 2 (every management write rewrites the whole file and reorders it) then stops mattering,
because nobody edits the file by hand. The template sets the defaults in section 3 and section 7,
request-retry: 0 so no hidden retries happen (as in row 96), the client-key list holding only the
sentinel key (D11), and the management key (D6).
2. Credential entry
D6. Management key. Jason sets the management key once, at a human-only prompt
(mosaic proxy init, run in his own shell, path-bound human proof as for other human verbs, #225). The
prompt doesn't echo the key. It writes the key to <configDir>/secrets/proxy-management.key (0600) and into
the initial config.yaml. The proxy hashes it in place on its first start (row 96 point 6). The stack reads
only the secrets file, never the hashed value in config. The holder is the bus process alone (D12). The
sandbox doesn't mount <configDir>/secrets, so no session can read the key, and no verb returns it. Jason
types the key only at that prompt. The Console's proxy page needs only his Console sign-in, and the bus adds
the key to its own management calls (Q250). An agent never runs the prompt (Q173).
D7. Upstream credentials. Accounts are entered in the Console's proxy page (Q250). It covers OAuth
sign-ins for Claude and Codex through the management API's oauth/auth-url, oauth/callback and
oauth/status, upstream API keys (z.ai first, write-only and never echoed, mos-1608), and account
priority. The page shows each account by an id, the first 12 hex of the sha256 of the proxy's own
credential identifier, never of a token or key value (mos-1608). The credentials live in the
proxy's auth/ directory and config and nowhere else. There is no per-seat credential file. The stack never
reads, copies or logs an upstream credential.
D8. Dropped (Q250). The design had the Console forward https://mosaic.woltje.com/proxy/ to the
proxy's own panel. Jason's Q46 and Q47 answers replace it with the Console's own proxy page (D7). The page
calls bus routes, the bus calls the management API (D12), and the management key stays in the bus (D6). No
new hostname is added. A panel subdomain is a fallback only if our page can't keep up with the management
API across pinned proxy upgrades, and that call is Jason's. Each pinned upgrade runs the page's acceptance
script against the new version (P4 brief item 9).
3. Priority routing, and how the business routing table maps onto it
D9. Who picks what. The business routing table picks the harness, model and thinking for a role
(packages/business/src/vars.mjs:139-147, resolved by sessionModel in packages/harness/src/bundle.mjs:77-86).
The proxy picks which upstream account serves that model. The two don't overlap:
- The model id in the business table is the id the session sends. The proxy routes it to an account that serves that model.
- The business
fallback.*vars stay the stack's. They still apply only when the primary harness's preflight fails (packages/cli/src/launcher.mjs:390-397). - Account failover inside one model is the proxy's job. The stack doesn't model accounts per session.
D10. Priority settings. Defaults set in the template are listed below (Q222: settings with defaults). Jason sets account priority in the Console's proxy page. The other routing settings keep the template defaults, and a page control for them is a later row (Q250).
routing.strategy: fill-first. A subscription account is used until it is rate-limited or out of quota, then the next one (config.example:150).- Each account gets a
priority(larger first,config.example:480). Jason sets the order in the Console's proxy page (J4). routing.session-affinity: true, so a session stays on one account while it works and keeps its prompt cache (:154-171). Defaultsession-affinity-rebind-on: quota, so a transient 5xx doesn't move a session.- No model aliases or alias pools by default. A pool that maps one name to several upstream models (:869) would let the proxy change the model under a role. Model choice stays in the business table, where a reviewed edit can see it. If Jason adds an alias himself, that's his call.
launch.models (packages/business/src/launch.mjs:12-24) gets a provider value proxy, meaning "this model id
through the instance proxy". launch.account (:81-89) with a proxy names the proxy rather than an
environment variable set. The row that implements it settles the exact key name. A business without
proxy providers keeps working unchanged.
Not settled by the design, and checked in the build row: whether 8.0.27 can restrict a client key to some models. If it can, an agent's key is narrowed to its role's models as a second guard. If not, the bundle's model choice is the only guard, as it is today.
4. Per-agent keys
D11. Key format and the sentinel. A stack-issued client key is msk-<instanceTag>-<32 random bytes, base64url>.
The prefix tells stack keys apart from any key Jason adds himself. The stack manages only keys with its own
prefix and leaves every other key alone. One sentinel key, msk-<tag>-sentinel-..., is written by the
template and never handed out. It keeps the list non-empty, so the proxy never falls into a possible open
mode on an empty list (row 96 point 5). The build row tests the empty-list behavior on a stand-in, and the
result goes in BUILD.md either way. The bus refuses to launch (proxy-open) if a GET of the list shows
no sentinel.
D12. Who calls the management API. The bus process only, through a new packages/model-proxy/ client. It
reads the management key from D6's file, holds it in memory, and sends it only in the Authorization header to
loopback. No CLI verb, Console route or session tool exposes it. Every management call is logged as a
receipt (call, status, key id) with no key value.
D13. The stack's record. The key is a row 199 store kind, model, one per agent:
<configDir>/secrets/agents/<business>/<agent>/model (0600). This replaces D7's "per stack instance only"
for instances with a proxy. The file holds the key value, so the bus can hand it to a session. Its status
record (Credentials.status) holds a key id (the first 12 hex of its sha256), the issue time and the state.
It never holds the value. The record is authoritative. The proxy's list is derived state, and the bus
reconciles it.
- Boot. The bus GETs the list once and compares it with the record. A key that is in the record but not in the proxy is re-PUT. A stack-prefixed key that is in the proxy but not in the record, or is revoked there, is removed. The reconcile writes a receipt.
- No role-file change. The model key needs no
credentials.modelreference in the business file. Every agent of an instance with a proxy gets one implicitly. The tradeoff: a business can't opt a role out. The other way would addmodelto every role definition, which means changingroles/*.json, and that is a reviewed policy change for no benefit today.
D14. Issue, rotate, revoke.
- Issue.
instance.hire(packages/business/src/instance-settings.mjs:354) and the first launch of an agent without a key both callCredentials.issue(model). The bus GETs the list, adds the key and PUTs the list. It then GETs again and checks the key is present before it writes the store file. If any step fails, no file is written and the bus refusesproxy-key-issue-failed. - Rotate.
rotateBy(row 199 D2) defaults to 30 days for model keys (Q222 setting). The bus PUTs the new key alongside the old one, writes the new file, and launches new sessions with the new key. It removes the old key once the agent's runs that started on it have ended, or afterproxy.rotateGraceMinutes(default 120), whichever comes first. A session still running at that point is stopped. That's 3 calls with no restart, and the two keys overlap for the window (row 96 rotation item). - Revoke. Fire already stops the agent's runs before it delists the agent (row 194). The revoke step in
runtime.mjs revoke()gains the model kind: a GET, then a PUT of the list without the key, then a GET to confirm. The store file goes only after that. If the revoke fails, the fire is refusedcredential-revoke-failed, as for the other kinds. Revoke never uses the v0DELETE ?value=<key>, because it writes the full key into the access log (row 96 point 1). The v8 PUT doesn't.
D15. Plaintext at rest, accepted. The proxy keeps client keys in plaintext in config.yaml, and GET api-keys
returns them in full (row 96 point 1, queue note a). This is accepted, with these limits:
- the file and the store sit under the instance user's 0700 directories, which no session mounts;
- the management client never logs a list body, and the GET response stays in memory only;
- the usage reader (section 7) maps a key to its id at read time and stores only the id.
A key leak lets someone spend through the proxy until the key is revoked. It never exposes an upstream credential.
5. Harness config written from the proxy
D16. One source. At launch the bus resolves the session's route and the agent's key, and passes the harness
module { baseUrl: "http://127.0.0.1:<port>", key } alongside today's env answer. Each harness module writes
its own config from that. It's rewritten on every launch, so a port or key change reaches the next session
with nothing cached. No harness reads ~/.claude, ~/.pi or ~/.codex of the host (managed-session-config.md:36-39).
When the proxy is configured, sessionEnv stops passing the instance-wide CLAUDE_CODE_OAUTH_TOKEN,
ANTHROPIC_*, OPENAI_API_KEY, OPENROUTER_API_KEY and ZAI_API_KEY. A session gets only its own key, so a
proxy-mode session can't reach an upstream with the instance's token.
- Claude Code (
packages/plugins/harness/claude-code/index.mjs,env:119-126):ANTHROPIC_BASE_URLandANTHROPIC_AUTH_TOKEN=<key>. This already satisfiesSESSION_CREDENTIALS(session.mjs:249-253) and passesENV_ALLOW. Row 96 ran exactly this. - pi (
packages/plugins/harness/pi/index.mjs,env:63-70): writes<run>/home/.pi/agent/models.jsonwith{"providers":{"anthropic":{"baseUrl":"<baseUrl>"}}}(no/v1), and setsANTHROPIC_API_KEY=<key>(row 96 pi finding, :123-136). For a non-Anthropic route (z.ai through the proxy), the same file names that provider's base URL. pi gains a preflight that refusesno-session-credentialwhen neither a proxy key nor a pi credential is present. This settles P19 #383 for proxy-mode instances: pi gets a key of its own, and nobody mapsCLAUDE_CODE_OAUTH_TOKENinto a pi variable. Whether that closes #383 is Jason's call. - Codex. The stack has no Codex harness plug-in yet. This design fixes its proxy config for when one
exists:
CODEX_HOME=<run>/home/.codex, withconfig.tomlnaming amodel_providerwithbase_url = "<baseUrl>/v1",env_key = "OPENAI_API_KEY"andwire_api = "responses", andOPENAI_API_KEY=<key>. The Codex harness row writes it. The T3 Codex threads the development seats use are not stack sessions and are out of scope.
6. Row 96's open points
| # | Point | Disposition |
|---|---|---|
| 1 | Plaintext keys in config, the usage queue and the v0 DELETE log | Accepted at rest (D15). Avoided in logs: no v0 DELETE (D14), and the usage reader stores ids (section 7) |
| 2 | Every management write rewrites the whole config | Solved: nobody edits the file after creation (D5) |
| 3 | No per-key accounting, 60 s queue window | Solved by the stack counting its own tokens (REQ-LIMIT-2). The queue reader is an optional plug-in that polls faster than the window (section 7) |
| 4 | Revoked or wrong key looks like a hang in Claude Code | Solved without relying on the client (D17). The 401-shape cause stays unverified and no shape fix is in scope. The build row tests it on the stand-in and records the result (queue note b) |
| 5 | Empty key list unknown | Solved by the sentinel (D11). Tested on a stand-in, result recorded |
| 6 | Management key hashed in place at startup | Solved: the stack reads only its own secrets file (D6) |
| 7 | pi ignores ANTHROPIC_BASE_URL |
Solved: pi gets a session models.json (D16) |
| 8 | Stand-in limits: no real API, rate limits, token counting or routing | Accepted for the build rows, which run on stand-ins. The first real upstream traffic is Jason's cutover step (Q127), J5 |
| a | GET api-keys returns every key in full |
Accepted, in memory only (D15) |
| b | 401 shape as the stall cause, unverified | Tested before any shape fix. None is planned (point 4) |
D17. Key and proxy preflight. Before a launch in proxy mode, the bus makes one request with the agent's key,
GET /v1/models. The build row confirms on the stand-in that this is answered locally with no upstream call. A
401 refuses the launch with proxy-key-refused. A connection failure refuses with proxy-down. Neither starts a
session, so a revoked key never reaches Claude Code's silent retry. A key that is revoked mid-session is
revoked by the stack, which stops that session first (D14). Idle before the first actual adapter turn
is unarmed. The successful adapter-child spawn arms one initial proxy.firstOutputSeconds budget
(default 120) per proxy run; later turns never reset or extend it and first output cancels it. A turn
still silent when that budget expires is stopped through the existing trusted path and logged as
proxy-stall. Invalid first-turn metadata also uses that same stop and value-free receipt once,
unless first output already cancelled the watcher. The internal timestamp protocol uses the current
Linux host/runner shared monotonic clock; a confined timestamp-bound check is required verification.
7. Usage and limits
D18. What counts. The stack counts the tokens its own sessions use, per agent key and per account, as
REQ-LIMIT-2 says (docs/prd/mosaic-stack.md:964, packages/tasks/requirements.json:57). It's the default and
only built-in source. The existing launch.limits.tokens keys (keys, daily;
packages/business/src/launch.mjs:60-75) get the agent key id as a valid keys name, so a daily quota per agent
key is a business setting. It defaults to unset, which means no per-key quota.
D19. The plug-in. Under Q153/Q154, a usage plug-in kind takes one optional module, cliproxy-queue. It
drains GET /v8/management/observability/usage/queue?count=100 through the bus's management client every
usage.cliproxyQueue.pollSeconds (default 15, under the 60 s retention). It maps each event's api_key to a key
id, then drops the value. It records upstream failures (fail.status_code) per key id. It is off by default,
and turning it on is a plugins.set change. When on, it is a second source that the Console shows next to the
stack's own count. It never enforces a quota alone. A queue event for a key with a msk- prefix that isn't
in the record goes in the reconcile log.
D20. Settings and who changes them. Every number in this design is a setting with the default shown:
rotateBy 30 d, proxy.rotateGraceMinutes 120, proxy.firstOutputSeconds 120, usage.cliproxyQueue.pollSeconds
15, routing strategy fill-first, affinity on. The per-key daily quota and the rotation period guard the human
from agents, so only a human can change them (Q222). The routing settings live in the proxy and only Jason
changes them. Account priority is in the Console's proxy page, and the rest keep their defaults (Q250).
8. Row 157 re-scope (#1652, Vikunja 617)
Row 157 is on hold (Q248, mos-1568/1569), and this design doesn't start it. When it resumes, its brief is amended this way, and Mos rules the amendment:
- Egress. The
agentsnetwork's allowed list is the proxy container only. api.anthropic.com, api.z.ai and api.openai.com come off the agents list (bentina/row157's draftcontracts/egress.json) and go to a newproxylist. The proxy container sits onmosaic-agentsandmosaic-egress(containerized-install-design.md:786-830), and its CONNECT calls are logged to<dataRoot>/state/egress.jsonllike any other. - The real-token run is cancelled.
provider-hosts.shandprovider-runner.mjspassedCLAUDE_CODE_OAUTH_TOKENandZAI_API_KEYinto a CLI child. That run is not done, and is replaced by a stand-in run. A session container reaches the stand-in only through the proxy container, and a direct connection to an upstream name is refused. - Decision 108's mounted token file (
/run/mosaic/secrets/claude-code-oauth.token) is not mounted into session containers of a proxy-mode install. The proxy container holds the upstream credentials in its own volume.
9. Migration
claude-session.env (host installs) and decision 108's file (containerized) stay until each instance switches.
An instance switches only when its config gains the proxy section (D3). Until then nothing about it
changes. Cutover, per instance, with Jason present (Q127):
- The build rows land and pass the landing gate. They don't change any instance yet.
- Jason fetches and verifies the pinned binary and panel (J1). ops-01 or Sage checks the sha256 against PIN.
- Jason adds the
proxysection to the instance config (J2), runsmosaic proxy init(D6), and startsmosaic-proxy.service. The bus isn't restarted yet. - Jason adds the accounts in the Console's proxy page (J3, J4). No session uses the proxy yet.
- A Console or bus restart (Jason or Mos, per Q173) picks up the section. The bus reconciles, issues a key for each listed agent, and runs D17's preflight for each one. Any refusal stops the cutover, and the fix is to remove the section and restart.
- One test session per harness, launched by the tester actor, makes one real turn through the proxy (J5). This is the first real upstream traffic on the new path.
- Once both harnesses pass, the bus unit drops
EnvironmentFile=for claude-session.env. The file stays on disk for a 7-day rollback window, then Jason deletes it (J6). Rollback is removing the section and restoring theEnvironmentFile=line.
Order: the dragon-lin default instance first, then .53, then Melanie's .54 hub once Jason has picked its accounts (J7). The 8317 host proxy and its clients are not part of any step.
10. Jason's steps
- J1. In a browser, open the CLIProxyAPI release page for the version in PIN and download the named asset and the panel asset into the folder the cutover note names. Sage checks the hashes. (If Mos assigns the fetch to a row instead, J1 drops.)
- J2. Add
"proxy": { "port": <port from the cutover note>, "binary": "<path>" }to the instance'sconfig.json, in an editor. - J3. In a terminal of your own, run
mosaic proxy initand type a new management key twice. Keep it in your password manager. You don't type it anywhere else (Q250). - J4. Open the Console's proxy page and sign in to Authentik as jason.woltje. Sign in to each subscription account you want this instance to use (Claude, Codex). If the sign-in tab doesn't return on its own, copy the full address from that tab and paste it into the page. Set each account's priority. The highest one is used first, and the next one only when the first is limited. Paste each API key you want it to have (for example z.ai) into the page's API-key field. The page never shows it again.
- J5. Watch the Console while the two test sessions run (cutover step 6), and confirm the proxy page's usage section shows their requests on the account you expect.
- J6. After 7 days on the proxy, delete
claude-session.envfor that instance. - J7. For Melanie's .54 hub, decide which accounts it uses: hers, yours, or new ones. Then do J2 to J4 on .54 through its own Console's Authentik provider (Q218). Only accounts you entered for that hub go into its proxy.
Build rows (proposed, each briefed after Mos rules)
All are Codex builds on new gpt-6.1-sol threads (Q246), first review by a separate Codex thread. They touch credentials, so a Claude reviewer (Dewey) gives the second review. Sage runs the landing gate.
| Row | Piece | Depends on |
|---|---|---|
| P1 | packages/model-proxy/: PIN, template, management client, mosaic proxy init, the mosaic-proxy unit, config proxy section, stand-in tests (empty list, /v1/models local, 401 shape, OAuth callback URL; the panel checks are dropped, Q250) |
Mos's ruling on D3 |
| P2 | Per-agent keys: model store kind, issue, rotate, revoke, reconcile, D17 preflight |
P1, rows 194 and 199 |
| P3 | Harness config: Claude Code env, pi models.json and preflight, sessionEnv proxy mode |
P2 |
| P4 | The Console proxy page and its bus verbs (Q250, 2026-10-24_proxy-page-brief.md) |
P1; lands after P2 |
| P5 | usage plug-in kind and the cliproxy-queue module; key ids in launch.limits.tokens |
P2 |
| P6 | Row 157 brief amendment (section 8) | Row 157 resuming |
| P7 | Cutover note and per-instance window (section 9) | P1 to P4 landed; Jason present |
The Codex harness config (D16) goes with the Codex harness row, which isn't on this list.
What Mos rules
- D1/D3: the
proxysection in the instance config (a schema change), and the proxy as its own user unit. - D6: the management key set by Jason at a human-only prompt and held only by the bus process.
- D8: ruled. Jason's Q46 and Q47 (Q250) drop the forward for the Console's own proxy page.
- D13: an implicit per-agent model key with no role-file change.
- D15: plaintext keys at rest accepted under those limits.
- D16: proxy mode drops the instance-wide provider variables from every session.
- J1: Jason fetches the binary, or a row does.