ibexharness
DocsBlogReleasesRoadmap
GitHub
ibexharness

Documentation

Architecture Decision RecordsADR-0002: Repository foundation bootstrapADR-0003: Branch protection and merge policyADR-0004: Protobuf and code generation policyADR-0005: Postgres migration strategyADR-0006: Auth protobuf contract (`ibex.auth.v1`)ADR-0007: Auth token validation implementationADR-0008: Security scanning and CI quality gatesADR-0009: Permission bitmap layoutADR-0010: Cryptography policyADR-0011: Proxy auth gRPC client and middlewareADR-0012: Proxy request normalization (OpenAI chat)ADR-0013: Proxy input validation and stable error envelopeADR-0014: Core domain migration sequencingADR-0015: Proxy rate limit skeleton (Phase 1)ADR-0016: Proxy agent identity verification (Phase 1)ADR-0017: Request ID and trace context strategy (Phase 1)ADR-0018: Graceful shutdown contract (Phase 1)ADR-0019: OpenTelemetry provider configuration (Phase 1)ADR-0020: Shared package boundaries — `packages/config` and `packages/apierror`ADR-0021: Prometheus Metric Catalog (Phase 1)ADR-0022: Health check contract (Phase 1)ADR-0023: Docs site architecture (Phase 1.5)
ADRs›ADR-0016: Proxy agent identity verification (Phase 1)
ADRs

ADR-0016: Proxy agent identity verification (Phase 1)

Architecture decision record 0016.

ADR-0016: Proxy agent identity verification (Phase 1)

  • Status: Accepted
  • Date: 2026-06-06
  • Authors: IBEX Harness team

Context

Milestone 1.2.3 extracts X-IBEX-Agent-ID as a parseable UUID but does not verify that the agent belongs to the authenticated organization. An Org A token combined with Org B's agent UUID creates a cross-tenant confusion risk. Auth service M1.1.7 already implements ValidateAgent gRPC; the proxy must call it on every protected request.

Decision

1) Enforcement point

Agent ownership is verified in the proxy middleware, not in downstream memory/context services. Rationale:

  • Single enforcement point before any handler runs
  • Latency cost is already paid for token validation on the same gRPC connection
  • Downstream services receive a verified AgentRecord in context

2) gRPC to auth, not direct DB

Proxy calls AuthService.ValidateAgent via the existing *grpc.ClientConn. Proxy must not query Postgres for agents.

3) Bearer forwarding

ValidateAgent requires caller authentication (auth unary interceptor). Proxy re-parses the Authorization header and forwards authorization: Bearer <token> as outgoing gRPC metadata. Tokens are never logged.

4) org_id source

org_id passed to ValidateAgent comes from auth.FromContext (token claims), never from the request body, URL, or X-IBEX-Agent-ID header alone.

5) Required header

X-IBEX-Agent-ID is required on all protected Phase-1 routes:

  • GET /v1/internal/auth-probe
  • GET /v1/orgs/{org_id}/auth-probe
  • POST /v1/chat/completions

6) Anti-enumeration

Auth returns PERMISSION_DENIED (not NOT_FOUND) for cross-org and missing agents. Proxy maps to 403 AGENT_NOT_AUTHORIZED — never 404.

Inactive agents (paused, suspended, archived) return PERMISSION_DENIED with message "agent is not active". Proxy maps to 403 AGENT_SUSPENDED. Other PERMISSION_DENIED cases map to AGENT_NOT_AUTHORIZED.

7) Fail-closed vs fail-open

ControlFailure behaviorHTTPcode
Token validate (M1.2.1)Fail closed503SERVICE_DEGRADED (ADR-0011; unchanged)
Agent verify (M1.2.5)Fail closed503AUTH_UNAVAILABLE
Rate limit (M1.2.4)Fail open——

Agent identity is a security control; failing open during auth downtime would allow cross-tenant agent confusion.

8) HTTP mapping (agent middleware)

ConditionHTTPcode
Header absent400MISSING_AGENT_ID
Malformed UUID400VALIDATION_ERROR + field_errors
Cross-org / not found403AGENT_NOT_AUTHORIZED
Inactive agent403AGENT_SUSPENDED
gRPC timeout / transport error503AUTH_UNAVAILABLE

9) Middleware order

Amends ADR-0013 §8 and ADR-0015 §6:

POST /v1/chat/completions:
  bodyLimit → contentType → auth → agentVerify → rateLimit → handler

GET /v1/internal/auth-probe:
  auth → agentVerify → rateLimit → handler

GET /v1/orgs/{org_id}/auth-probe:
  pathOrgUUID → auth → agentVerify → rateLimit → handler

10) Context

Verified agent stored as AgentRecord{ID, OrgID, Status} via AgentFromContext. Chat handler no longer validates agent header (middleware owns it).

11) Phase 2 caching (deferred)

Same pattern as token validation: bloom filter → LRU → gRPC fallback (milestone 2.2.1).

12) Configuration

Reuses IBEX_AUTH_VALIDATE_TIMEOUT (default 50ms) for ValidateAgent per-call deadline on the shared auth gRPC client.

Consequences

Positive

  • Closes cross-tenant agent confusion attack
  • Consistent with multi-tenant security rules (403 not 404)
  • Reuses auth service as source of truth

Negative

  • Second gRPC call per protected request (mitigated by shared connection; Phase 2 cache planned)
  • AGENT_SUSPENDED mapping relies on stable auth gRPC message "agent is not active" until richer error details exist

References

  • Milestone 1.2.5
  • ADR-0011
  • ADR-0013
  • ADR-0015

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0015: Proxy rate limit skeleton (Phase 1)NextADR-0017: Request ID and trace context strategy (Phase 1)

On this page

  • Context
  • Decision
  • 1) Enforcement point
  • 2) gRPC to auth, not direct DB
  • 3) Bearer forwarding
  • 4) org_id source
  • 5) Required header
  • 6) Anti-enumeration
  • 7) Fail-closed vs fail-open
  • 8) HTTP mapping (agent middleware)
  • 9) Middleware order
  • 10) Context
  • 11) Phase 2 caching (deferred)
  • 12) Configuration
  • Consequences
  • Positive
  • Negative
  • References
0%