Skip to content

tracker: multiple inference credential sets per account #2018

Description

@jhgaylor

Two customers want the same sentence and need different things.

The reseller. One account is really N end customers. He needs each end
customer's turns on that customer's inference credential, never another's.

The quota-juggler. One tenant, several Claude subscriptions, exhausting
quota on one and wanting the next agent to run on another.

Today inference_credentials is one row per user with four fixed ciphertext
columns and a unique_constraint(:user_id). InferenceCredentials.select/4
is the whole selection rule, and it reads that one row.

Direction

The reseller's answer is principals, not credential sets. ADR 0044 already
wrote the objection down -- "one shared service account is not isolation" --
and Principals.billing_subject_id/1 already gives him one invoice with
per-end-user usage attribution. One gap blocks it (item 10 below).

The juggler's answer is credential sets: inference_credentials becomes
1:N, an agent names its default and a launch may override it.

Relationship to ADR 0052

ADR 0052 (user-owned ChatGPT grants, #2010) is merged as Proposed, and
#2011-#2015 + #2017 build its sections 1, 3 and 6. Sections 4 (selection)
and 5 (sandbox identity)
are unbuilt, and that is exactly the ground this
work stands on. This tracker builds section 4's selection plumbing in the
shape 0052 asks for, so the ChatGPT work lands on top of it rather than
colliding, and amends 0052's "no per-agent preference in the first version".

Stack

  1. ADR 0053 + index refresh
  2. select/4 returns a source struct, not a bare atom (pure refactor)
  3. Fix: an env/vault secret shadowing a static inference credential resolves
    the source to :own (P1, mis-bills on prod today)
  4. Inference credential pairs move off the shared .env into process env
  5. inference_credentials gains name + is_default; 1:N
  6. agents.inference_credential_id + allowed_inference_credential_ids
  7. conversations.inference_credential_id per-launch override
  8. API: CRUD, OpenAPI, contract, TS SDK
  9. Console
  10. An owner may set an inference credential on a principal it owns
  11. Docs, CHANGELOG, SDK bump

The bug in item 3

PlatformInference.gate/3 and select/4 both ask has_own?, which reads only
the inference_credentials table. A vault secret named ANTHROPIC_API_KEY
is invisible to it, but wins in the sandbox env (egress.ex:223-226, and
docs/concepts/secrets.md:212 publishes the rule). So on a deployment holding
platform keys -- ours since 2026-09-03 -- the turn is selected :platform,
stamped "platform" by TurnMachine.with_inference/2, priced by
CreditPricer, counted against PLATFORM_INFERENCE_DAILY_CENTS, and can be
refused by check_ceiling/0, while the tenant's own vault key actually serves
it. #1941 fixed an adjacent facet today; this one stands.

ADR 0052 section 4 already mandates the fix: "Environment/vault API-key
overrides must resolve into the same explicit source decision."

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:billingStripe, subscriptions, metering, quotasarea:securitySecurity and tenant isolationtrackerParent issue coordinating multiple deliverables; do not count as a separate unit of work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions