Skip to content

Name vendors after their APIs and split placeholder delivery - #179

Merged
vitramir merged 3 commits into
mainfrom
vendor-naming-and-openai
Aug 8, 2026
Merged

Name vendors after their APIs and split placeholder delivery#179
vitramir merged 3 commits into
mainfrom
vendor-naming-and-openai

Conversation

@vitramir

@vitramir vitramir commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Contracts for OpenAI Subscriptions and Vendor Naming.

Additive only — buf breaking against main is clean, and so is buf lint.

Vendors are named after APIs

claude/codex become anthropic/openai. Per the spec this is the cause of the OpenAI gap rather than cosmetics: the old row paired chatgpt.com — the subscription host a Codex CLI calls at /backend-api/codex/responses — with OPENAI_API_KEY, the variable that puts a Codex CLI in API-key mode addressing api.openai.com. Two mutually exclusive configurations in one row, because the row was named after the client rather than the API it opens.

The old names are retained as aliases on the same numbers. Nothing on the wire changes, and no consumer has to recompile in lockstep:

enum Vendor {
  // buf:lint:ignore ENUM_NO_ALLOW_ALIAS
  option allow_alias = true;
  VENDOR_UNSPECIFIED = 0;
  VENDOR_ANTHROPIC = 1;
  VENDOR_CLAUDE = 1 [deprecated = true];
  VENDOR_OPENAI = 2;
  VENDOR_CODEX = 2 [deprecated = true];
}

Generated code carries both constants at the same value. JSON accepts either name and emits the first declared, so VENDOR_ANTHROPIC becomes canonical on output while "VENDOR_CLAUDE" still parses on input. allow_alias is banned by the STANDARD lint set, hence the targeted ignore — verified that the rename fails buf breaking without the alias and passes with it.

The alias does not remove the migrations, it sequences them. Three things still hold the old names and are the risk the spec names: subscription rows (vendor TEXT plus its CHECK constraint), the provisioned OpenZiti services and Dial policies, and the llm-native-<vendor> role attributes stamped on live workload identities. A workload holding llm-native-claude after its service is renamed dials something that no longer exists and silently stops reaching its vendor.

Placeholders are typed

A placeholder is the dummy credential an agent CLI needs to start, which the proxy discards and replaces. Codex reads its subscription credential from ~/.codex/auth.json rather than an environment variable, so the mechanism gains a second kind — and the kinds have different writers:

Kind Written by Why it survives into a sandbox shell
ENV Agents Orchestrator, onto the container spec The runner's Exec carries no environment, so the session inherits the container spec's
FILE agynd, at container start The filesystem outlives the writer, so a session exec'd hours later finds it

SubscriptionAttachment therefore carries placeholder_kind plus the variable name (ENV) or the path and contents (FILE), so neither writer holds a vendor table of its own. placeholder_env keeps its field number and its meaning for the ENV kind.

Sequencing

This lands first — every service regenerates from the published module. Implementation follows in llm, agents-orchestrator, agynd-cli, llm-proxy, bundle-vm, agyn-cli, console-app, and e2e, along with the three migrations above.

@vitramir
vitramir requested a review from a team as a code owner August 8, 2026 20:06
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow buf-pr / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedAug 8, 2026, 9:38 PM

vendor is renamed anthropic|openai. Naming the enum after CLIs is what
produced the incoherent OpenAI binding: the row paired chatgpt.com --
the subscription host Codex calls -- with OPENAI_API_KEY, the variable
that puts Codex in API-key mode addressing api.openai.com instead. A
credential belongs to the API it authenticates; which CLI presents it is
incidental.

The old names stay as aliases on the same numbers, so nothing on the
wire changes and no consumer has to recompile in lockstep. That leaves
the database rows, the OpenZiti services, and the role attributes on
live identities as the only things needing migration -- and lets them
migrate one at a time rather than together.

A placeholder is now typed. Codex reads its subscription credential from
a file rather than an environment variable, and the two kinds have
different writers: a variable must be on the container spec for a
sandbox shell to inherit it, while a file lands at a CLI-specific path
under HOME that only agynd can resolve. The attachment carries the kind
and everything its writer needs.
@vitramir
vitramir force-pushed the vendor-naming-and-openai branch from 48bb30b to 5da419a Compare August 8, 2026 21:21
@vitramir
vitramir merged commit 9301ec1 into main Aug 8, 2026
1 check passed
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.

1 participant