Repository navigation
Description
Audit follow-up — antigravity + vibe credential paths spawn secret-tool; antigravity refresh requires the agy binary
Source: 2026-11-06 provider audit (Audit B: binary-required providers). The wire-only doctrine ("no provider the fleet uses requires a local binary") does not hold on the credential path for the two keyring-based doors. Whole-tree check: grep -rnE "child_process|Bun.spawn|execa" over packages/ai/src/{providers,protocols,route} returns exactly two files — these two.
antigravity-direct (source id antigravity) — hard dependency
Inference is wire (POST daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent). The credential path is not:
antigravity-auth.ts:103-111: every credential load dynamically importsnode:child_processandexecFilessecret-tool lookup service gemini username antigravity(15s timeout).findSecretTool()searches$SECRET_TOOL_PATH,$PATH,/nix/store/*libsecret*/bin/secret-tooland throwssecret-tool not found— no non-binary credential tier (static override aside).- Token refresh requires the
agybinary file:findAgyBinary()probes$AGY_BIN,~/.gemini/bin/agy,/run/current-system/sw/bin/agy;binaryIntel()reads its bytes to extract GOCSPX OAuth client-secret candidates;refresh()fails hard (could not extract OAuth client secret from agy binary) when absent. A still-valid token works without it; an expired one cannot be refreshed. (The binary is read, never executed; the CLI also owns writing the keyring credential.) - Same pattern in the quota layer:
quota/providers/antigravity.ts:124alsoexecFilessecret-tool(gracefulnullthere).
vibe-direct (source id vibe) — fallback tier that is the deployed tier
Inference is wire (MistralChat route, api.mistral.ai/v1); no vibe subprocess exists. But vibe.ts:133 wires VibeAuth.vibeAuth, whose tier-4 resolution (vibe-auth.ts:105-118) execFiles secret-tool lookup. It is nominally a fallback (env → ~/.vibe/.env → keyring, failures fall through) — except the fleet config deliberately keeps MISTRAL_API_KEY out of the server env (#1653) so the deployed door falls through to the keyring sign-in key: in actual fleet use, the credential is obtained by spawning secret-tool.
Desired end state
Credential resolution that reads what the one-time CLI sign-in provisioned without spawning a helper binary — e.g. read the keyring via a Secret Service D-Bus library binding instead of the secret-tool subprocess, or make a file/env tier the deployed default with the keyring tier removed. claude-code / commandcode already prove the read-in-place pattern (file read only, zero spawns). For antigravity refresh, the agy-binary byte-extraction should be replaced by a provisioned client-secret source (vaulted at sign-in time) so refresh survives the CLI's absence.
Related: muse-direct's layer-2 dependency (its local muse-proxy spawns the Muse CLI) is tracked with the proxy decommission pass in developing-today/code, not here.
Acceptance criteria
- The credential paths for the antigravity and vibe providers no longer hard-require a local binary (
secret-tool/agy) without a documented fallback tier, OR the wire-only doctrine is amended in writing to name these two credential-path exceptions explicitly. - On a host without
secret-tool, a credential load for each affected provider either succeeds via the fallback tier or fails with an error that names the missing binary and the supported alternatives (no opaque throw). - Regression coverage exists for the no-binary path (simulated missing
secret-tool) for both providers.
Activity
dezren39 commentedon Nov 6, 2026
🤖 Triage evidence · issue-sweep-rotating (Make feeder)
22c0a6ddbee19878e8e51bde432e995f75ccdd2a(verified x2 viagh api), reading the named files raw viagh apicontents.packages/ai/src/providers/antigravity-auth.ts:99—findSecretTool()throwssecret-tool not foundwhen no binary is locatedantigravity-auth.ts:103-111— every credential load dynamically importsnode:child_processandexecFilessecret-tool lookup service gemini username antigravity(15s timeout)antigravity-auth.ts:128/:145/:333— refresh goes throughfindAgyBinary()+binaryIntel(): the OAuth client-secret candidates are extracted from theagybinary's bytes, so an expired token cannot be refreshed where the binary is absentpackages/ai/src/providers/vibe-auth.ts:105-118— tier-4 resolutionexecFilessecret-tool lookup; per the issue (and vibe provider: native wire-protocol emulation of the Mistral Vibe binary (no ACP host, no subprocess) #1653 keepingMISTRAL_API_KEYout of the server env) this fallback tier is the deployed tier for the vibe doorpackages/ai/src/quota/providers/antigravity.ts:124— the quota layer alsoexecFilessecret-tool(gracefulnullthere)secret-toolsubprocess, or a file/env tier made the deployed default with the keyring subprocess tier removed); antigravity refresh no longer depends on extracting the client secret from theagybinary's bytes (provisioned/vaulted at sign-in time); theclaude-code/commandcoderead-in-place pattern (file read only, zero spawns) is the shape to match.repo-task/v1triage packet intorepo-health-opencode-pilotfrom this feeder run; the queue lane owns the fix work.dezren39 commentedon Nov 6, 2026
🤖 Agent note · queued by triage feeder
repo-task/v1triage packet as task 10989 in queuerepo-health-opencode-pilot(kindtriage, priority 50, deduperepo-health:v1:triage:developing-today/opencode:issue:1825). Packet:hidden_files/packet-opencode-1825.json.22c0a6ddbee1(verified x2):antigravity-auth.ts:103-111execFilessecret-tool, refresh depends onagybinary bytes (:128/:145/:333);vibe-auth.ts:105-118tier-4 does the same.dezren39 commentedon Nov 6, 2026
Intake review
packages/ai/src/{providers,protocols,route}spawning processes) is the verification anchor.antigravity-auth.tscredential load, theagyrefresh probe) are presented with file:line evidence in the body. This is a doctrine-vs-implementation fork: either the code gains a non-binary credential tier or the doctrine gets a written exception; the intake does not pre-judge which.🤖 Fleet monitor intake (repo-fleet-monitor), 2026-11-06.
dezren39 commentedon Nov 6, 2026
🤖 Agent note · triage verdict: CONFIRMED, routed as split
Verified fresh on current origin/main (laptop git-show + gh raw reads):
Route (split):
13 remaining items
dezren39 commentedon Nov 6, 2026
codex-proxy (:18744) — RETIRED 2026-11-06 (Drew, proxy-infra decommission)
Closing the loop on this audit's codex item: the
openaiprovider was verified wire-only here, and Drew's question — "is this not [what] the openai direct provider replaces?" — is now answered in production state, not just in the audit.Replacement proof
packages/core/src/plugin/provider/openai.ts): direct HTTPS — issuerhttps://auth.openai.com, ChatGPT/Codex plan basehttps://chatgpt.com/backend-api/codex, plan auth in-process; no subprocess, no proxy hop, nothing references :18744.credential/codex-sync.ts(feat(core): import Codex CLI credentials in-fork with true expiry #1468, the Door front migration: codex-proxy (18744) callers repoint to the façade #1154 cutover) reads the Muse auth file into the credential store — it never contacts :18744.route_attempt, laptop server): ~198k successful attempts in 7 days via nativeopenai/openai.main/openai.alternate; the proxy-fronting ids (chatgpt,openai-codex) last attempted 2026-10-24, definitions deleted 2026-11-03, legs re-seated onto openai-plan.Consumer census at kill time (all negative): live config chain carries zero :18744 values (comments only, no
{env:CODEX_PROXY_PORT}template left); the socket had never activated the service this boot (no journal entries, NRestarts=0); OpenClaw held only anAfter=mention (its model route is the :13420 facade); the quota gatherer is in-binary; the PATH 1add729 binary reads the same config with no :18744 values to consume.Kill: socket stopped + disabled (disable also unlinked the HM unit symlink → not-found), service inactive,
tailscale serve:18744 forward removed, port closed on loopback + tailnet. Post-kill probe: 37/37openai.mainattempts succeeded in the 3 minutes after the kill, port closed throughout.Repo removal: developing-today/code PR anomalyco#6596 (merge
bba05833b5) — bin program + suite deleted, HM service/socket/session-var/launcher/codexProxyPortlet removed, guard row dropped, catalog-sync registry trimmed, OpenClaw/PORTS/docs swept; 200/200 bin tests, port-guard script green, both editeddefault.nixparse clean. HM side takes effect at the next switch (anomalyco#5647); the live disable holds until then.One deliberate survivor (also noted on the PR):
provider-catalog-sync'schatgptsource entry remains as the enumeration machinery's worked example for the anomalyco#3449/anomalyco#4588 CLI fixtures — dormant (no live config names achatgptprovider); removing it is a fixture redesign, not a decommission step.Merge pull request #1872 from developing-today-forks/muse-direct-native
dezren39 commentedon Nov 6, 2026
Coordination note for the muse-direct lane: the fastlane cutover at 2026-11-06 13:07 CST (fork main
1b27880fb0, drop-in20-fastlane.conf, GC rootfastlane-gcroot-1b27880) already carries #1872 (muse-direct native Meta wire) — it was deployed together with the #1873/#1875 wire-identity fixes in a single build/restart, so no separate muse-direct deploy is needed. Full record: #1876 (comment 6693505058). Note the build required the nix/hashes.json nodeModules restamp (stale pin blocked all builds of main); that fix has since landed upstream as PR #1880.dezren39 commentedon Nov 6, 2026
Addendum: after the note above was posted, the record was completed by this lane's own 13:17 CST cutover to the pure
08eca393d6d1build (see #1876 supersession note) — production now runs that build, which carries #1872 as noted. No action needed; this just keeps the sequence accurate.dezren39 commentedon Nov 6, 2026
muse-direct audit item — CLOSED with evidence (2026-11-06)
Drew's order: "what part of muse proxy sounds direct." muse-direct is now a native fork provider; muse-proxy is dead.
Design (Option B, as built)
New native provider in the fork, id
muse-direct(unchanged), model ids unchanged (muse-spark-1.3-contributor,muse-wonder-1.0-contributor); routes/seats untouched. Three new files:packages/ai/src/providers/muse-direct-auth.ts— in-process exchange mirroring the proxy's direct mode exactly: reads~/.config/muse/auth.json,providers.meta.access_token→POST https://api.meta.ai/muse-code/key(Authorization: Bearer <account token>,x-api-version: 1.0.0,{"onboard": false}) → minted key; 10-min in-memory cache;is_subs_active === false/require_payment === trueare hard errors with the proxy's verbatim messages; api_key-only auth files used directly; missing credential fails closed. No subprocess, no CLI, no PAYG fallback.packages/ai/src/protocols/muse-direct-responses.ts— wrapper around theMetaResponsesprotocol carrying the tool-name attestation aliasing (shell→run_command, bash→terminal, with the collision + prototype guards) at the protocol layer, since session traffic never passes through core Generate (whose aliasing is scoped to providermeta). Plus a 401-retry transport (invalidate → re-exchange → retry exactly once). Route defaultsstore: false,include: ["reasoning.encrypted_content"], mirroringmeta.packages/ai/src/providers/muse-direct.ts— provider package mirroring meta.ts/claude-code.ts (single Responses route, endpointhttps://api.meta.ai/v1).Pool discipline preserved: the
metaprovider (PAYG door) is untouched; themuseprovider id stays disabled; muse-direct ≠ meta (different credentials, routes, ids).Effort parity defect — found in scratch, fixed in the same PR
Recorder capture showed session bodies with
reasoning: nullfor variant sessions. Root cause is fork-wide: config variants in the v1 dialect ({options: {reasoning_effort: "xhigh"}}) migrate to variant settings{options: {reasoning_effort}}, and the native dialect resolver only reads flat camelCasereasoningEffort— the door honored the dialect (the proxy maps it at muse-proxy.mjs:1252), so cutting over naively would have silently stripped the seats' effort. muse-direct'sfromSettingsnow normalizesreasoningEffort/reasoning_effort/options.reasoningEffort/options.reasoning_effort→reasoningEffort(the antigravity provider's top-levelreasoning_effortis the house precedent). The same latent drop affects every native provider whose config variants use the v1 dialect — themetaprovider block included. Flagged here as a fork-wide follow-up; this change fixes muse-direct only. Proven end to end: scratch session bodies carryreasoning: {effort: "xhigh"}with the production-shaped config. Tests:packages/ai/test/provider/muse-direct.test.ts10/10; ai+core typecheck clean; full ai suite failure set identical to the pristine-main baseline (12 pre-existing recorded-fixture failures).PRs / merges
nix/hashes.json, breaking every nix build of main) merged as 08eca39.Scratch verification (recording listener in the path)
Isolated scratch server (worktree source) with a recording forwarder to api.meta.ai: 11 requests, all POST https://api.meta.ai/v1/responses; pid-attributed socket sampling showed the server process connected ONLY to the recorder — zero connections to :18747. Wire auth fingerprint matched the auth file's stored key (Meta accepted it — the answer was a quota wall, not a 401/403). Tool aliasing proven on real session traffic (18 function tools, no
bash/shell,run_commandpresent).Production cutover + probes (verbatim)
Fastlane repoint to
/nix/store/dy8kimkyrdb9byppbwnbd4x4hpapqxm0-opencode-2.0.30+08eca39-wrapped(rollback: the 5e9b135 drop-in backup). Both pinpoint seats probed on production, route attempts recorded:muse-direct/muse-spark-1.3-contributor#xhigh→ 429 "Subscription quota exhausted. Your usage window resets at 2026-11-09T00:00:00Z."muse-direct/muse-wonder-1.0-contributor#xhigh→ same 429 verbatim.Meta's full body:
{"error":{"code":"rate_limit_exceeded","message":"Subscription quota exhausted. Your usage window resets at 2026-11-09T00:00:00Z.","param":null,"resets_at":1794182400,"type":"rate_limit_error"}}— a wall answer BY META over the direct wire is the pass condition while the pool is walled; no PAYG funds spent. Zero connections to :18747 from the server process during the probes (pid-attributed sampling).Quota
opencode quota(new build) keeps reading the Meta pool: the quota provider (packages/ai/src/quota/providers/meta.ts) has no proxy dependency — CDP portal read primary, fallback POSTsmuse-code/keywith the auth.json account token. Gatherer output (quota-current.json, 2026-11-06T19:15:29Z, post-cutover) carriesmetawith stateok, weekly 100%, resetsAt 2026-11-09T00:00:00Z, credits $148.57. (The human text view omits the Meta section under BOTH the old and new builds — pre-existing; the JSON the gatherer consumes is complete.)muse-proxy death certificate
systemctl --user stop muse-proxy+disable: unit not-found, inactive; port 18747 not listening (connection refused).bin/muse-proxy.mjs,bin/muse-proxy-start.sh,bin/muse-proxy.test.mjsdeleted;museProxyPortlet,systemd.user.services.muse-proxy, and theMUSE_PROXY_PORTsession variable removed fromhome/common/default.nix(dated retirement notes left); port-guard row removed (guard passes vacuously, stays wired); PORTS.md row retired; OpenClaw TOOLS.md row removed, QUOTAS.md + prompt/let comments corrected; historical route-narration comments left as history.~/.config/opencode/) and/home/user/.opencode/opencode.jsonc— the latter proved to be the same inode (hardlink) as the repo file, so it tracks the repo automatically; the runtime overlay contains no muse-direct.nixos-rebuild switch; until then the disabled state holds (unit file removed, nothing restarts it).dezren39 commentedon Nov 7, 2026
Survivor closed: the dormant
chatgptcatalog entry is removedDrew ruled on the one disclosed survivor of the codex-proxy kill (recorded above, comment 6692223967): remove it and redesign the fixtures (2026-11-06 19:18 CST).
Landed as developing-today/code#6657, merged
d13f07c7:bin/provider-catalog-sync.mjs—PROVIDER_SOURCESno longer contains thechatgptproxy entry (dead :18744). (Note: provider-catalog-sync lives in developing-today/code'sbin/, not in this fork.)'fixture-proxy'(fixture port :18799, nothing listens there by design). Rationale: every surviving real source is crosscheck/unverifiable kind, and the source table has no injection point, so the proxy-kind validated-addition path can only be exercised through a proxy-kind entry — the fixture now says so in its name instead of borrowing a dead product's.bin/provider-catalog-sync.test.mjs: the runSync snapshot fixtures and the CLI control fixtures (fix: $ invocation not .quiet() anomalyco/opencode#3449/Input area to clunky (for me) in 1.0.85 anomalyco/opencode#4588, incl. the overlay regression test) run throughfixture-proxy/18799.json; tests 37/37 pass (node --test).chatgptstrings are unrelated living features/history (openai-cred-watchdog credential-method names, the chatgpt-desktop nix package, a VS Code extension id, historical removal comments in.opencode/opencode.jsonc, upstream console i18n copy) — untouched, no follow-up issue needed.dezren39 commentedon Nov 7, 2026
Correction to the comment above: the merge commit for developing-today/code#6657 is
82c715a3a45994eb0c98ed78be141625d83a93b3(merged 2026-11-07T01:21:23Z, per the merge command's own readback), notd13f07c7as misstated above. Everything else in that comment — the change, the fixture design, the 37/37 test result, and the residue sweep — stands as written.dezren39 commentedon Nov 8, 2026
🤖 Agent note · triage revalidation: CONFIRMED, routed — resume of task 10989 (attempt 2)
Revalidated fresh on current origin/main
b3778ebdda6d1fc04d33717b12384625f849b402(gh api + laptop git show, 2026-11-08T02:15Z). Issue open, no held / user-owned / do-not-merge / do-not-auto-edit labels.Spawn sites still present, with current file:line:
packages/ai/src/providers/antigravity-auth.ts:112findSecretTool();:156-164every credential loadexecFilessecret-tool lookup service gemini username antigravity(15s timeout);:175findAgyBinary()+:192-270binaryIntel()extract OAuth client-secret candidates (GOCSPX) from theagybinary's bytes; refresh depends on them at:316and:414.packages/ai/src/providers/vibe-auth.ts:118-131— tier-4execFilessecret-tool; this is the deployed tier (vibe provider: native wire-protocol emulation of the Mistral Vibe binary (no ACP host, no subprocess) #1653 keepsMISTRAL_API_KEYout of the server env).packages/ai/src/quota/providers/antigravity.ts:1,124— importsexecFilefromnode:child_processandexecFilessecret-tool(graceful null).Routing (per Drew's ruling, comment 6686794578 — no provider may depend on running a binary command for auth):
repo-health-opencode-pilot(kindsmall_fix, deduperepo-health:v1:fix:developing-today-forks/opencode:issue:1964). Chosen no-spawn route is specified in the packet: in-process Secret Service (D-Bus) keyring reads at all three sites, or the provisioned read-in-place credential-file tier if D-Bus proves infeasible in Bun — either way zero spawns; antigravity client secret provisioned at sign-in (credential-store field orANTIGRAVITY_OAUTH_CLIENT_SECRET),findAgyBinary/binaryIntelremoved from refresh; vibe deployed-tier resolution preserved.triagedwith follow-up task 11785.Note: a first enqueue of the same packet landed in the wrong queue (
repo-health-code-shadow, task 11784) because no explicit--queueflag was passed; it was cancelled and superseded by 11785 in the correct queue.dezren39 commentedon Nov 9, 2026
🤖 Agent note · pointer (from task 14259 triage, lane opencode-a)
The fork-wide v1-dialect
reasoning_effortfollow-up flagged on this issue ("the same latent drop affects every native provider whose config variants use the v1 dialect — the meta provider block included") is now owned by #2203. Triage on #2203 (2026-11-09) audited every native provider's effort settings path: meta, vercel-ai-gateway, alibaba, and amazon-bedrock-mantle drop the v1 spellings; antigravity and commandcode conform for top-level spellings only; the recommended fix normalizes once inProvider.nativeSettings(packages/core/src/provider.ts). This issue remains scoped to the secret-tool audit follow-up only.