IBEX Harness
DocsBenchmarksBlogChangelogRoadmap
GitHub
IBEX Harness

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)ADR-0024: Benchmark data publishing modelADR-0025: LLM provider abstractionADR-0026: OpenAI client designADR-0027: Streaming dual-write strategyADR-0028: Auth cache designADR-0029: Token revocation propagation via Redis pub/subADR-0030: Directive versioning strategyADR-0031: System prompt injection strategyADR-0032: Session data model and retentionADR-0033: ClickHouse llm_traces schema and retentionADR-0034: Proxy overhead performance measurement methodologyADR-0035: Chat Idempotency-Key Redis dedupeADR-0038: Context assembly service design and gRPC contractADR-0039: Proxy Postgres ownership for session and directive storesADR-0040: Anthropic provider adapterADR-0041: Model capability registryADR-0042: Self-hosted OpenAI-compatible LLM adapterADR-0043: Tokenizer registry architectureADR-0044: Non-streaming response pipelineADR-0045: Streaming response transformationADR-0046: Embedder interface and profile registryADR-0047: Memory temporal validity foundationADR-0048: Memory multi-label categoriesADR-0049: Memory relationship graph readinessADR-0050: MCP server skeleton (transport, auth, audit)ADR-0051: Local LGTM observability stack (Phase 2.5 exit pull-forward)ADR-0052: Memory schema v2 expand (HNSW, quality columns)ADR-0053: Vector store abstraction and composite scoring v2ADR-0054: In-process Presidio PII detection in memoryADR-0055: Memory write-path exact and near-duplicate dedupADR-0056: Temporal-interval-aware conflict detectionADR-0057: Memory write-path persistence and orchestrationADR-0058: Semantic search FTS fallback triggerADR-0059: Hot cache read pathADR-0060: Memory service tenant isolation architectureADR-0061: Retrieval-quality benchmark methodologyADR-0062: Worker task observability and dead-letter handlingADR-0063: Extraction output schema v2ADR-0064: Extraction provider registry and batch executionADR-0065: Extraction batch idempotency and turn pointer orderingADR-0066: Extraction quality evaluation harnessADR-0067: Context capability catalog generate-and-diff for token budgetsADR-0068: Composite scoring-time relevance gateADR-0069: Context packer bounded DP knapsackADR-0070: Context formatting locked ordering and session nonceADR-0071: Context assembly gRPC degradation ladder and retrieval deadlineADR-0072: Extraction enqueue via worker HTTP (not Celery Redis wire format)ADR-0073: Organization Lifecycle and Cascading SuspensionADR-0074 Provider Credential StorageADR-0075 Per-Org Model Routing PolicyADR-0076: Circuit Breaker per ProviderADR-0077: Provider Fallback RoutingADR-0078: Operator runtime topology & environment contract (4.P.0)ADR-0079: Operator session RS256 dual-verify and TOTP step-upIBEX Console service rename and boundaryADR-0081: Fail-closed Proxy runtime controlsD2 metadata-only trace read contractD2 publication, identity, query, and lifecycle contract
ADRs›ADR-0073: Organization Lifecycle and Cascading Suspension
ADRs

ADR-0073: Organization Lifecycle and Cascading Suspension

Accepted — org suspension semantics, Redis revocation event_type, and last-owner invariant.

ADR-0073: Organization Lifecycle and Cascading Suspension

  • Status: Accepted
  • Date: 2026-09-09
  • Authors: IBEX Harness team
  • Milestone: 4.A.2 Org & User Management API

Context

The system stores tenant lifecycle on ibex_core.organizations.status with values such as active and suspended. Suspension must become effective quickly (same overall SLA as auth revocation invalidation), and the proxy must deny suspended-org access rather than letting tokens continue to work until TTL expiry.

IBEX already has a Redis pub/sub mechanism for token revocation propagation (ADR-0029). That channel is global and its primary invalidation primitive today is token-id scrubbing in the auth cache.

This ADR defines how to:

  1. Fail-closed on ValidateToken when the caller's org_id is suspended.
  2. Propagate suspension to proxy cache instances quickly.
  3. Keep authorization semantics consistent by using both users.role (org hierarchy) and the presented token's permission bitmap (ADR-0009).
  4. Enforce the last-owner invariant safely at the API layer.

Decision

1) Suspension vs deletion semantics

  • Suspension is reversible and must block proxy traffic quickly (fail-closed).
  • Deletion is irreversible and may require an ordered cascade job (GDPR).

Only suspension semantics are covered in this ADR; deletion cascade mechanics belong in the 4.A.2 implementation ADR and follow-up issues.

2) Reuse the existing revocation channel with an event_type

  • Channel: ibex:token:revocations (ADR-0029)
  • Event shape: JSON RevocationEvent extended with an event_type discriminator:
    • event_type = "token" for token revocations (existing behavior)
    • event_type = "org_suspend" for org suspension propagation

Backward-compatibility rule:

  • If event_type is missing, treat the event as event_type="token".
  • Token-revocation events must continue to include token_id and be valid under the previous schema.
  • Subscribers must treat org_suspend events as an alternative invalidation path that does not require a token_id → digest mapping.

3) Auth-side fail-closed check in ValidateToken

The AuthService.ValidateToken implementation must verify:

  • The token row is active and not revoked/expired (existing).
  • The token's org_id belongs to an organization with organizations.status = 'active'.

If the organization is not active, return a stable domain error representing org suspension. No proto wire changes are required if this can live in auth server-side logic.

4) Proxy-side cache invalidation and HTTP mapping

On the proxy, the revocation subscriber must handle:

  • event_type="token": call existing CachingValidator.InvalidateByTokenID(token_id)
  • event_type="org_suspend": call CachingValidator.InvalidateByOrgID(org_id) and scrub all relevant cached entries

HTTP error mapping:

  • Denials due to suspended org must map to a dedicated canonical API error code:
    • ORG_SUSPENDED
  • The mapping should use 403 (Forbidden) semantics to distinguish from invalid/expired tokens, ensuring suspended org denials do not poison the auth cache bloom filter intended for invalid-token caching.

5) Last-owner invariant enforcement (API-layer lock)

When demoting org roles (e.g. PATCH user role):

  1. Start a transaction under the org scope.
  2. Lock matching owner rows, then count them in application logic:
    • SELECT id FROM ibex_core.users WHERE org_id = $1 AND role = 'owner' AND deleted_at IS NULL FOR UPDATE
    • Deny when len(rows) <= 1 would leave the org without an owner.
  3. If the caller would demote the final owner, deny with:
    • 409 LAST_OWNER_PROTECTED

This is enforced in the API layer because database-only constraints cannot express "last owner" safely and ergonomically for this workflow, and because the API layer needs a deterministic error code.

6) Role ↔ permission-bitmap mapping (ADR-0009)

The management API must use both:

  • users.role for org hierarchy invariants
  • the presented token's ADR-0009 permission bitmap to gate whether the PAT is allowed to call management endpoints

This ADR documents the role → bitmap mapping used for interpreting the management surface:

  • owner → permissions.Admin
  • admin → permissions.Admin
  • member → permissions.AgentDefault
  • viewer → permissions.ReadOnly

If the presented token bitmap is missing required bits for a given management operation, deny even if the user's role would allow it; conversely, do not rely on token bitmap bits alone for last-owner protection.

Consequences

  • Suspended-org access becomes fail-closed quickly (cache invalidation + auth-side join/check).
  • The revocation propagation mechanism remains single-channel (ADR-0029) by adding an event_type discriminator.
  • The management API authorization semantics are deterministic and auditable: role enforces hierarchy, bitmap enforces token capabilities.

Alternatives considered

  1. Separate Redis channel for org suspension
    • Rejected: introduces second propagation mechanism with extra operational complexity.
  2. Only invalidate cache entries without auth-side checks
    • Rejected: brand-new/uncached tokens issued after suspension could still work until natural expiry.
  3. Add org suspension status to proto
    • Rejected: avoid unnecessary wire expansion if server-side logic is sufficient.

References

  • ADR-0029 Token revocation propagation via Redis pub/sub
  • ADR-0009 Permission bitmap layout
  • 4.A.2 Org & User Management API

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0072: Extraction enqueue via worker HTTP (not Celery Redis wire format)NextADR-0074 Provider Credential Storage

On this page

  • Context
  • Decision
  • 1) Suspension vs deletion semantics
  • 2) Reuse the existing revocation channel with an event_type
  • 3) Auth-side fail-closed check in ValidateToken
  • 4) Proxy-side cache invalidation and HTTP mapping
  • 5) Last-owner invariant enforcement (API-layer lock)
  • 6) Role ↔ permission-bitmap mapping (ADR-0009)
  • Consequences
  • Alternatives considered
  • References
0%