Summary
POST /altimate/base/register (added in #1266 to let non-TUI hosts like the VS Code extension trigger Altimate Base) accepts a hash that any caller can compute locally without ever fetching or displaying the actual disclosure text — so any local process that can reach altimate serve's HTTP port can register Altimate Base, mint a persistent per-install tracking identifier, and opt the install into request/response logging, without the consent dialog ever being shown.
Recalibration (see discussion below): this was initially filed as a security P0 ("any attacker can bypass consent"). On review, that framing overstated it — reaching this endpoint already requires local code execution, and an attacker at that privilege level can automate the TUI dialog just as easily as forming an HTTP POST, and has far more valuable things to do with that access than silently register a free model tier. There is no meaningful capability gained by a malicious local actor that they didn't already have.
The real, narrower issue: the consent gate is a UX affordance, not a security boundary (its own code comment in host.ts says so), and #1266 exposes a route that lets any co-resident, non-malicious process — a future Altimate integration, a CI job, some other local tool — flip a privacy-relevant switch invisibly, with no way for the user to see or decline it. That matters for the feature's informed-consent posture even though nothing malicious is implied. As of today this is a latent design gap, not an active exploit — no shipping client calls this route yet (confirmed: zero references to it in sdks/vscode).
Root cause (verified by tracing the code)
packages/opencode/src/server/server.ts — POST /altimate/base/register calls FreeTierHost.registerWithAcceptedDisclosure(acceptedDisclosureSha256).
packages/opencode/src/altimate/free/host.ts:79-88 — this checks the supplied hash against FreeTierConsent.disclosureHash().
packages/core/src/altimate-base-disclosure.ts:15-16 — disclosureHash() is a SHA-256 of a public, hardcoded string. Any caller can compute this locally without ever calling GET /altimate/base/disclosure, let alone rendering it to a person.
There is no session/UI-provenance check, no interactive-confirmation flag, nothing binding the POST to a human having actually read anything.
Proposed fix (scoped, <30 min, no design work)
Bind POST /altimate/base/register to a prior GET /altimate/base/disclosure call from the same caller via a short-lived, single-use nonce that must be echoed back on the register call. This doesn't prove a human read the screen, but it closes the "compute the public hash and skip the GET entirely" gap for any non-malicious co-resident caller, which is the actual risk here.
Provenance
Found during the v0.11.0 stable release's Chaos Gremlin (privacy/compliance) persona review. Deliberately shipped in v0.11.0 as an explicit, user-reviewed risk acceptance (recalibrated to a design-gap/P1, not a blocking security P0) — recorded here so the fix isn't lost.
Summary
POST /altimate/base/register(added in #1266 to let non-TUI hosts like the VS Code extension trigger Altimate Base) accepts a hash that any caller can compute locally without ever fetching or displaying the actual disclosure text — so any local process that can reachaltimate serve's HTTP port can register Altimate Base, mint a persistent per-install tracking identifier, and opt the install into request/response logging, without the consent dialog ever being shown.Recalibration (see discussion below): this was initially filed as a security P0 ("any attacker can bypass consent"). On review, that framing overstated it — reaching this endpoint already requires local code execution, and an attacker at that privilege level can automate the TUI dialog just as easily as forming an HTTP POST, and has far more valuable things to do with that access than silently register a free model tier. There is no meaningful capability gained by a malicious local actor that they didn't already have.
The real, narrower issue: the consent gate is a UX affordance, not a security boundary (its own code comment in
host.tssays so), and #1266 exposes a route that lets any co-resident, non-malicious process — a future Altimate integration, a CI job, some other local tool — flip a privacy-relevant switch invisibly, with no way for the user to see or decline it. That matters for the feature's informed-consent posture even though nothing malicious is implied. As of today this is a latent design gap, not an active exploit — no shipping client calls this route yet (confirmed: zero references to it insdks/vscode).Root cause (verified by tracing the code)
packages/opencode/src/server/server.ts—POST /altimate/base/registercallsFreeTierHost.registerWithAcceptedDisclosure(acceptedDisclosureSha256).packages/opencode/src/altimate/free/host.ts:79-88— this checks the supplied hash againstFreeTierConsent.disclosureHash().packages/core/src/altimate-base-disclosure.ts:15-16—disclosureHash()is a SHA-256 of a public, hardcoded string. Any caller can compute this locally without ever callingGET /altimate/base/disclosure, let alone rendering it to a person.There is no session/UI-provenance check, no interactive-confirmation flag, nothing binding the
POSTto a human having actually read anything.Proposed fix (scoped, <30 min, no design work)
Bind
POST /altimate/base/registerto a priorGET /altimate/base/disclosurecall from the same caller via a short-lived, single-use nonce that must be echoed back on the register call. This doesn't prove a human read the screen, but it closes the "compute the public hash and skip the GET entirely" gap for any non-malicious co-resident caller, which is the actual risk here.Provenance
Found during the v0.11.0 stable release's Chaos Gremlin (privacy/compliance) persona review. Deliberately shipped in v0.11.0 as an explicit, user-reviewed risk acceptance (recalibrated to a design-gap/P1, not a blocking security P0) — recorded here so the fix isn't lost.