128 lines
7.7 KiB
Markdown
128 lines
7.7 KiB
Markdown
# Model proxy (P1 #1827, P2 #1829)
|
||
|
||
The bus initializes and manages one pinned CLIProxyAPI per instance. PIN records
|
||
release URLs and independently reviewable SHA256s of the archive, executable and
|
||
panel. `mosaic proxy fetch` verifies both downloads before installing under
|
||
`<dataRoot>/opt/cliproxyapi/<version>/`; there is no runtime panel update.
|
||
|
||
From a human terminal, with the business bus running:
|
||
|
||
mosaic proxy fetch
|
||
mosaic proxy init [--port N] [--business ID]
|
||
|
||
Init prompts twice without echo and passes the key to the bus through its
|
||
path-bound human proof. An agent, tester, redirected stdin or mismatched
|
||
confirmation refuses. The management key must contain 1–4096 printable ASCII
|
||
characters with no spaces (bytes 0x21–0x7e). The bus exclusively creates proxy.json beside config.json,
|
||
the private management-key file and initial proxy directory. It never rewrites
|
||
config.json. Init does not install/start a service. The validated system config
|
||
contains `proxy.port` and `proxy.binary` only when proxy.json exists.
|
||
|
||
The management client is imported by the bus; it only GETs/PUTs the v8 list on
|
||
127.0.0.1, never follows redirects, and emits receipts with SHA256 key IDs only.
|
||
Client-key values are never returned by an agent-facing bus verb. The bus manages
|
||
per-agent lifecycle below; initialization creates only the sentinel.
|
||
|
||
The unit starts through a hash-checking runner with the pinned panel directory
|
||
and `-local-model`, so catalogs require no network. Render/install is explicit;
|
||
no test or acceptance script installs or starts a host unit.
|
||
|
||
Acceptance is agents/magica/work/queue-329/acceptance.sh, outside the generic gate.
|
||
It uses a fresh scratch root and private network namespace. The historical
|
||
v1.26.7 panel sandbox findings remain in BUILD.md. Q250 / mos-1606 drops the
|
||
Console panel forward; P4 builds a native Console page. P1 retains the pinned
|
||
panel asset and records Claude/Codex OAuth redirect URLs for that page.
|
||
|
||
A stale `.proxy-init.lock` has no automatic expiry. Only the human may clear it,
|
||
after stopping this instance's bus user unit so it cannot restart and confirming
|
||
that no initialization operation is active. Check the selected config/data root:
|
||
if `proxy.json`, `secrets/proxy-management.key` or `<dataRoot>/proxy/` exists,
|
||
retain that state and resolve the partial/completed initialization separately;
|
||
do not delete it to make init run again. If none exists and the bus is stopped,
|
||
remove only the stale lock, substituting the actual selected config directory:
|
||
|
||
rm -- "<configDir>/.proxy-init.lock"
|
||
|
||
Then start the bus normally and retry init from the human terminal. If the bus
|
||
or initialization state is uncertain, leave the lock in place. A refused fetch
|
||
may leave empty `opt/cliproxyapi/` parent directories; they are not an installed
|
||
version and need not be removed before retrying.
|
||
|
||
PIN upgrades must carry the native Console page's offline upgrade receipt.
|
||
Run `bash agents/poe/work/queue-330/acceptance.sh <verified-fetched-executable>`
|
||
under the test-slot rules, and include its output for the new PIN version in
|
||
that upgrade row's BUILD.md. Sage's landing gate checks the receipt. The script
|
||
uses a new temp root and private network namespace, with made-up credentials,
|
||
and calls the page's bus verbs. If the page cannot pass against the new PIN,
|
||
the upgrade stops; Sage takes Q46's fallback to Jason through Mos.
|
||
|
||
The native `/proxy` page uses human-only bus verbs; only the bus holds the
|
||
management client. On PIN 8.0.42, OAuth credentials are listed under
|
||
`/v8/management/credentials` and priority is patched as `{name, priority}`.
|
||
API-key groups are absent from that list: z.ai keys use the Claude-compatible
|
||
provider configuration at `/v8/management/config/api-keys/claude`, whose keys
|
||
carry proxy-generated `auth_index` identifiers. Their priority updates use
|
||
provider configuration and a read-back check. No upstream API-key value is
|
||
returned by the bus.
|
||
|
||
P2 adds an implicit `model` credential for every agent in proxy mode. The bus
|
||
confirms the v8 key-list write before storing a key at
|
||
`<configDir>/secrets/agents/<business>/<agent>/model` (0600, directories 0700).
|
||
The private file holds the key and its lifecycle metadata. Status and receipts
|
||
contain key IDs, issue time, rotation date and state; they never contain values.
|
||
No role or business `credentials.model` reference is needed or accepted.
|
||
|
||
Rotation defaults to 30 days. New launches use the replacement key. The old key
|
||
stays until its runs end or its grace expires; a failed bounded session stop
|
||
keeps both the old key and its retirement metadata for the next check. Revoke
|
||
requires a confirming management GET and polling `/v1/models` to 401 before local
|
||
cleanup. Polling has a five-second total deadline and 25ms intervals. A durable
|
||
`revoking` state blocks launches before preflight, and boot reconciliation
|
||
finishes it without restoring that key. Reconciliation preserves foreign keys
|
||
and refuses launches when this instance's sentinel is absent.
|
||
|
||
For a new instance only, human `proxy init` also accepts
|
||
`--rotate-grace-minutes N` (1–1440, default 120) and
|
||
`--first-output-seconds N` (1–3600, default 120). These are optional fields in
|
||
proxy.json; existing two-field sidecars remain valid. There is no per-agent
|
||
rotation-period setter in P2. Fetch also refuses agent-marked and tester calls
|
||
before reading config or touching the network/disk.
|
||
|
||
The trusted launcher requests `modelPreflight` with business/role and receives
|
||
exactly `{ baseUrl, key }`. That answer never passes through an agent socket or
|
||
status API. A 401 or other non-success HTTP status refuses `proxy-key-refused`;
|
||
a failed connection refuses `proxy-down`. No model response activity by the configured deadline
|
||
requests a bounded stop and records `proxy-stall` with key IDs. The runner writes
|
||
only a fixed token to `<run>/proxy-first-output`, never stdout contents. Managed
|
||
adapters also publish this token on trusted streamed model response events
|
||
(including thinking and tool-use), before their answer-only stdout. Startup
|
||
status, tool results and workspace file writes do not count. A stop
|
||
failure records/refuses `proxy-stop-failed` and retains the key.
|
||
|
||
P5 adds the bus-only `readUsageQueue()` management operation. Its fixed
|
||
`GET /v8/management/observability/usage/queue?count=100` retains the client's
|
||
existing response-size/deadline/loopback controls. It validates the entire array
|
||
(maximum 100 events), hashes client `api_key` and upstream `source` to 12 hex IDs
|
||
at the read boundary, and returns only safe IDs, counts and failure metadata.
|
||
Bodies, credential values and upstream error messages never enter receipts.
|
||
|
||
### Shared proxy for organization buses
|
||
|
||
Organization buses on one instance share the proxy using separate `mso-`
|
||
key namespaces. Their proxy management writes cooperate through an
|
||
instance-wide lock. A write with an uncertain outcome leaves a durable fence:
|
||
proxy-dependent launches and management mutations refuse until recovery, and
|
||
catalogues report unavailable. Check the fence with `mosaic proxy fence status
|
||
--json`. After actually restarting the proxy process so no old request can
|
||
still arrive, a human can run `mosaic proxy fence clear --proxy-restarted`.
|
||
The command reads the management list under the lock and records a receipt;
|
||
each organization's next start reconciles its own namespace. Recovery also
|
||
invalidates cached catalogues. The flag is the operator's statement that the
|
||
restart occurred; the command cannot verify it.
|
||
|
||
The lock is cooperative among this release's buses on the same instance. Older
|
||
buses, the proxy's own panel, other holders of the management key, and the
|
||
human `proxy fetch` and `proxy init` tools do not participate. Such writers can
|
||
still race a whole-list write or alter proxy state. A server-side fence is a
|
||
follow-up; do not run those writers concurrently with organization buses.
|