bcode_auth_broker_plugin 0.0.1-alpha.0

Single-owner GitHub OIDC model broker for Bcode
docs.rs failed to build bcode_auth_broker_plugin-0.0.1-alpha.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

Auth broker (single owner, Linux)

This plugin serves a normalized text-and-tools model gateway. It does not proxy arbitrary HTTP, store provider credentials, enroll subscriptions, or interpret provider wire formats.

Server admission and launch

The server owns the retained operation store, binds the global serve invocation, generates its correlation identity, reaps expired turns, cancels invocation-owned turns on exit/disconnect, and awaits workers during daemon shutdown. Only the explicitly admitted plugin's serve operation receives model-host routing. HTTP policy comes from trusted startup configuration, not the IPC payload. Existing bcode plugin call/invoke --daemon boundaries keep the call open while serving.

[plugins.config."bcode.auth-broker".model_host]
allowed_plugin_id = "bcode.auth-broker"
model_profile = "broker"
supported_account_use = true
max_turns = 128
max_scope_seconds = 900

[plugins.config."bcode.auth-broker".model_host.custody]
provider_id = "openai"
directory = "/var/lib/bcode/broker-custody"
identity_file = "/run/credentials/bcode/age-identity"

[plugins.config."bcode.auth-broker".serve]
bind = "127.0.0.1:8080"
trusted_tls_ingress = false
audience = "https://broker.example/actions"
max_leases = 128
lease_seconds = 900

[[plugins.config."bcode.auth-broker".serve.allow]]
repository_id = "123456"
repository = "owner/repository"
workflow_ref = "owner/repository/.github/workflows/agent.yml@refs/heads/main"
git_ref = "refs/heads/main"
subject = "repo:owner/repository:ref:refs/heads/main"

Launch using the selected executable/config/state root:

bcode plugin call bcode.auth-broker/v1 serve --daemon

Missing/malformed admission disables the gateway rather than breaking unrelated daemon startup. The fixed model profile must name an explicit provider/model/auth profile. Native materialization continues for non-refresh API-key use when custody is omitted. Subscription remote rotation requires retained custody: begin/commit callbacks bind the actual provider owner and the same retained source used before dispatch. No unfenced fallback is permitted.

Supported Linux service custody

The custody directory must already be initialized as CredentialCustodyStorage and hold the explicit profile with its registered method's complete credential record. Configure the auth profile's settings.device_seal = "off" explicitly for this age-only service path. The identity file must contain one age secret identity, be an absolute owner-only regular file (no final symlink), and fit the bounded reader. Credentials must use sshenv, no auth pool, and explicit device-seal policy off; existing device-sealed/native/passphrase factors fail closed. Fresh profile encryption keys come from sshenv's cryptographic entropy source. Identity values are zeroizing host memory and never enter wire contracts or broker-plugin config. The directory retains an exclusive owner.

Enrollment/migration launcher gap: normal bcode auth login writes a legacy vault, not this retained custody directory. Existing authentication alone is therefore not yet the complete connect-once path. An explicit authorized enrollment/import command must initialize retained custody through the provider-auth lifecycle APIs; this server does not guess, migrate or create it.

The server adapter uses centralized profile/target resolution and the existing provider-owned start bridge. Provider refresh begin/commit remains on that provider bridge; the broker never receives subscription access/refresh tokens. The operator connects the supported account once through the existing provider authentication enrollment, then configures the fixed model profile. Operator responsibility for permitted use does not imply that arbitrary accounts are supported.

Local serve payload

{
  "invocation_id": "launcher-generated-correlation",
  "bind": "127.0.0.1:8080",
  "trusted_tls_ingress": false,
  "audience": "https://broker.example/actions",
  "allow": [{
    "repository_id": "123456",
    "repository": "owner/repository",
    "workflow_ref": "owner/repository/.github/workflows/agent.yml@refs/heads/main",
    "job_workflow_ref": null,
    "git_ref": "refs/heads/main",
    "subject": "repo:owner/repository:ref:refs/heads/main"
  }],
  "max_leases": 128,
  "lease_seconds": 900
}

Public binding requires trusted_tls_ingress = true and operator-provided TLS ingress. Do not expose the plaintext listener directly. Ingress must also impose connection, header, request-rate and idle time limits. Do not log Authorization headers, JWTs, lease exchange responses, bodies, or provider material. There are no browser routes and no general platform/tenant management.

Lease/run lifetime

The default lease TTL is 900 seconds; explicit values must be in 1..=900. The host also rejects scopes exceeding its configured maximum (at most 900 seconds). Configure max_scope_seconds to match lease_seconds. There is no renewal, effect replay, or credential fallback. Exchange returns expires_at = min(exchange_time + lease_seconds, verified GitHub JWT exp): JWT expiry can shorten even the default lease substantially. The runner must reject a budget that exceeds the returned remaining lifetime minus ten seconds before starting model work. A 600-second default run can therefore fail with a short-lived JWT; an explicit 86400-second budget is unsupported in broker mode. Such failure must not switch to API-key mode or replay tool effects. Obtain a fresh OIDC JWT only before a new explicit run, never as automatic renewal of admitted work.

Expiry, invocation exit/disconnect and shutdown request cancellation and finish independently of HTTP connectivity. Cancellation acknowledgement means a request was admitted, not that remote work stopped. Finish acknowledgement requires observed successful cleanup; failed or ambiguous cleanup remains capacity-accounted until restart.

HTTP contract

  • POST /v1/leases: Authorization Bearer <GitHub Actions OIDC JWT>. Returns { "lease": "opaque secret", "expires_at": 123 }. Requires exact issuer, string audience, RS256 signature using the fixed GitHub JWKS endpoint, required time claims, and exact policy. Redirects/key URLs from JWTs are forbidden. Key caching/fetches and lease retention are bounded.
  • Other calls use Authorization Bearer <lease>.
  • POST /v1/turns: { "request_id": "stable-id", "messages": [{"role":"user","content":[{"type":"text","text":"hello"}]}], "tools": [], "tool_call_policy": {"choice":{"mode":"auto"}}, "tool_schema_mode": null, "max_output_tokens": 1024 } returns { "turn_id": "opaque-host-id" }.
  • POST /v1/turns/{id}/poll: { "after_sequence": 0 } returns a bounded page of sequenced normalized events, next_sequence, and terminal.
  • POST /v1/turns/{id}/cancel and /finish: no body; acknowledgement {}. Finish returns cleanup_pending until provider cancellation/cleanup completes; retry is safe.

Message roles and contents use normalized ModelMessage: text, assistant tool calls and matched text tool results. Tool execution stays with the calling agent, not the broker. Definitions and schemas are bounded, external schema references rejected, and tool selection/schema capabilities must be guaranteed by both fixed model and provider. Images are unsupported. Unknown/private message fields, model/profile/auth/environment/provider settings cannot cross the HTTP contract. Output includes normalized tool-call events, text, usage and terminal events. Errors retain only normalized category/retryability with a generic safe message; private metadata, reasoning, continuation/context records and upstream diagnostics are excluded.

Start reserves the request identity before dispatch. Identical requests under the same lease return the same turn ID; changed payloads conflict. Start acknowledgement means reservation, not provider success. Poll reports normalized failure without upstream payloads. Ambiguous starts are never repeated automatically; their reservation remains capacity-accounted until restart.

Cursors replay retained events; an evicted/future cursor returns cursor_gap (410), not an incomplete successful stream. Retention is process-local: neither leases nor turns survive restart. Expired scopes cannot read or mutate turns. Provider cancellation and finish run independently of HTTP connectivity. Cleanup failures remain accounted rather than silently evicted. Target discovery, start and lifecycle calls have host deadlines (30/60/30 seconds). A timed-out start remains ambiguous and is never reissued. Configure bounded provider-native network timeouts too; dropping a host await cannot prove remote work stopped. Shutdown waits for admitted lifecycle workers.

Validation

Behavioral tests cover strict wire decoding, bearer ambiguity, exact workflow/repository claims, algorithm rejection before key fetch, lease capacity/expiry/JWT shortening, host ownership/idempotency, cursor gaps and terminal page draining. Direct host integration tests load the real bundled fake provider and exercise normalized tool continuation, cancel/finish acknowledgement, invocation loss, expiry, shutdown and failed-start reservation retention. These offline tests do not establish real provider or GitHub deployment acceptance. Coordinator owns formatting/build/test runs and integrated provider/HTTP verification.