Skip to content

WRK-2948: managed Habitat delegated identity and Ticino OBO lifecycle - #16

Open
nicky-isaacs-awoo wants to merge 36 commits into
mainfrom
datadog/patch/WRK-2948-managed-sandbox-context
Open

nicky-isaacs-awoo wants to merge 36 commits into
mainfrom
datadog/patch/WRK-2948-managed-sandbox-context

Conversation

@nicky-isaacs-awoo

@nicky-isaacs-awoo nicky-isaacs-awoo commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Related issue

Part of epic WRK-2948 (Omnigent trial). Supersedes #14, which targeted main; this PR targets datadog/main per the Omnigent Developer Guide branch model.

Summary

Motivation

Omnigent currently turns an OIDC login into its own self-contained session JWT. That is enough to authenticate later HTTP requests, but it discards the upstream Ticino credential needed to perform an on-behalf-of exchange when a managed Habitat sandbox is created, woken, relaunched, or deleted. Those lifecycle operations can also happen after the original request, so they must recover the exact owner's identity without falling back to another user or to a shared service credential.

What changed

  • OIDC callbacks now store the verified provider ID/refresh tokens as AES-GCM ciphertext in a workspace-scoped oidc_sessions row and give the browser/CLI an opaque sess_... handle. Request authentication resolves that handle off the event loop; logout revokes it and erases its encrypted credentials.
  • Token refresh is bound to the original issuer, client, subject, and user, and uses a short lease plus compare-and-swap versioning so multiple Omnigent processes do not race a refresh.
  • Managed-sandbox creation carries an identity-token provider derived from the authenticated request. The host row records only non-secret session/resource references so wake, relaunch, deletion, and cleanup can reconstruct the same owner-bound provider later. Missing, revoked, expired, or mismatched identity fails closed; the explicit local single-user flow remains supported.
  • Failed provider deletion revokes local access immediately and leaves a durable cleanup tombstone. A bounded background reconciler retries deletion by the exact recorded provider resource ID and refuses to create an ambiguous second sandbox generation.
  • A Docker/Postgres local harness, fake Ticino/Hab services, and an opt-in real Habitat check exercise the full login-to-OBO handoff.

ELI5: Omnigent keeps the signed-in user's Ticino credentials encrypted on the server and remembers which credential owns each managed Habitat. Whenever that Habitat needs lifecycle work, Omnigent acts as the same user; it never silently substitutes a different identity.

Ticino login -> opaque Omnigent session -> encrypted, workspace-scoped credentials
                                                       |
                                                       v
                                  create / wake / relaunch / delete
                                                       |
                                                       v
                                      Habitat OBO as the owning user

HTTP and database impact

  • No new public HTTP routes are added. Existing /auth/callback responses issue an opaque session handle when the OIDC store is configured, and /auth/logout now revokes that stored provider session before clearing the cookie.
  • Existing managed-session create, wake/relaunch, and delete paths now apply the owner-bound identity context. The session-delete response adds cleanup_pending so callers can distinguish confirmed provider deletion from deferred cleanup.
  • Migrations add the workspace-partitioned oidc_sessions table, refresh-coordination columns, and non-secret managed-lifecycle columns on hosts (sandbox_session_id, sandbox_credential_session_id, sandbox_lifecycle_state, and sandbox_cleanup_attempts). OIDC lookups include workspace_id, matching the composite indexes and preventing cross-workspace resolution.
  • OIDC mode now requires a 32-byte hex OMNIGENT_OIDC_CREDENTIAL_KEY, separate from the cookie-signing secret.

Test Plan

  • Targeted OIDC/session-store/token-refresh, managed lifecycle, callback, local-harness, and Habitat integration test modules passed.
  • Broader auth/app/tunnel tests passed after preserving the explicit local single-user managed-sandbox flow.
  • uv run --extra dev pre-commit run --all-files passed, including Ruff and Pyrefly.
  • Manual end-to-end verification completed locally: sign in to Omnigent through Ticino, exchange the signed-in identity with the Omnigent Kubernetes workload credential, and use the resulting OBO token against the real Habitat API.

Demo

  • Visual demo attached below
  • Non-visual evidence provided below or in Test Plan
  • Not applicable — no behavioral change

This is a server-side authentication and managed-lifecycle change; the reproducible local harness and manual real-Habitat flow are described in the Test Plan.

Type of change

  • Bug fix
  • Feature
  • UI / frontend change
  • Refactor / chore
  • Docs
  • Test / CI
  • Breaking change

Test coverage

  • Unit tests added / updated
  • Integration tests added / updated
  • E2E tests added / updated
  • Manual verification completed
  • Existing tests cover this change
  • Not applicable

Coverage notes

The automated suite covers workspace isolation, opaque-session authentication and revocation, refresh coordination, identity binding, lifecycle recovery, cleanup tombstones, and fake-service OBO behavior. The real cross-repository Habitat check remains opt-in because it requires an internal hab-launcher checkout and live credentials; that path was also verified manually against the real Habitat environment.

Changelog

Managed Habitat sandboxes now use the signed-in user's renewable Ticino identity throughout provisioning and lifecycle operations.

@github-actions github-actions Bot added the size/XL Pull request size: XL label Sep 16, 2026
@nicky-isaacs-awoo
nicky-isaacs-awoo force-pushed the datadog/patch/WRK-2948-managed-sandbox-context branch from b7c4915 to 33c4c0e Compare September 18, 2026 02:19
@nicky-isaacs-awoo nicky-isaacs-awoo changed the title WRK-2948: managed sandbox delegated identity (context primitive, OIDC token provider, launch capture) WRK-2948: managed Habitat delegated identity and Ticino OBO lifecycle Sep 18, 2026
@nicky-isaacs-awoo
nicky-isaacs-awoo force-pushed the datadog/patch/WRK-2948-managed-sandbox-context branch from 4a3a4ee to 2754d1e Compare September 18, 2026 13:13
@nicky-isaacs-awoo
nicky-isaacs-awoo marked this pull request as ready for review September 18, 2026 14:13
@nicky-isaacs-awoo
nicky-isaacs-awoo requested a review from a team as a code owner September 18, 2026 14:13
@nicky-isaacs-awoo
nicky-isaacs-awoo changed the base branch from datadog/main to main September 18, 2026 14:13
Co-authored-by: Nick Isaacs <nick.isaacs@datadoghq.com>
Introduce omnigent/onboarding/sandboxes/context.py as the portable,
framework-neutral boundary for carrying the signed-in user's identity
context (session, user, renewable token provider) across the managed
sandbox provisioning path via ContextVar semantics.

Co-authored-by: Nick Isaacs <nick.isaacs@datadoghq.com>
AuthProvider gains an optional get_identity_token_provider method.
OIDC mode resolves the request's sess_ handle through the same
validated credential path as get_user_id and returns a provider bound
to that credential session and its verified owner; every call
re-validates and returns or refreshes the current ID token via the
token manager. Header and accounts modes default to None, and
mismatched, missing, or revoked sessions fail closed.

Co-authored-by: Nick Isaacs <nick.isaacs@datadoghq.com>
The session-create route resolves the owner's identity-token provider
and binds a ManagedSandboxContext around only the asyncio.create_task
call that schedules the background launch. The copied context reaches
launcher construction and provision through asyncio.to_thread without
any launcher signature or factory change; the request task keeps no
scope after scheduling.

Co-authored-by: Nick Isaacs <nick.isaacs@datadoghq.com>
Cross-boundary integration test composing the full chain end to end:
an authenticated OIDC request scopes the managed-sandbox context
around the background launch, the real ProductionHabSandboxLauncher
reads the delegated Ticino ID token through the real OIDC token
manager, and a fake exchange client trades it (workload bearer
supplied separately, audience hab) for the OBO bearer that CreateHab
carries to a fake Habitat gRPC service. Skips unless
OMNIGENT_HAB_LAUNCHER_PATH points at the hab_launcher package.

Co-authored-by: Nick Isaacs <nick.isaacs@datadoghq.com>
Records the pre-deployment red-state expectations and the acceptance
sequence for delegated Habitat provisioning, with the prerequisite
pairing gates.

Co-authored-by: Nick Isaacs <nick.isaacs@datadoghq.com>
@nicky-isaacs-awoo
nicky-isaacs-awoo force-pushed the datadog/patch/WRK-2948-managed-sandbox-context branch from 227a85f to beb7009 Compare September 20, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Pull request size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants