Skip to content

feat: add developer console and phase-1 for sandbox lifecycle (OSEP-0006)#835

Open
divyamagrawal06 wants to merge 13 commits into
alibaba:mainfrom
divyamagrawal06:feat/osep-developer-console
Open

feat: add developer console and phase-1 for sandbox lifecycle (OSEP-0006)#835
divyamagrawal06 wants to merge 13 commits into
alibaba:mainfrom
divyamagrawal06:feat/osep-developer-console

Conversation

@divyamagrawal06
Copy link
Copy Markdown
Contributor

Summary

  • Implements OSEP-0006 Phase 1, orignally requested in feat: add developer console for sandbox operations with phased auth model #348.
  • Adds a console/ React SPA for day-to-day sandbox management (list, detail, create, renew, delete, get endpoint) so operators no longer need raw API access for common workflows.
  • Adds a config-gated server-side auth/authz layer (auth.mode = "api_key_and_user") with role-based access (read_only / operator) and metadata-based owner/team scoping.
  • Default config (auth.mode = "api_key_only") is unchanged; existing API key automation is unaffected.

Testing

  • Not run (explain why)
  • Unit tests
  • Integration tests
  • e2e / manual verification - Done both, added playwright integration tests, committed the screenshots as well as tested thoroughly w/ docker.

Breaking Changes

  • None as auth.mode defaults to api_key_only. No existing behavior changes unless operators explicitly opt in.
  • Yes (describe impact and migration path)

Checklist

  • Linked Issue or clearly described motivation - closes feat: add developer console for sandbox operations with phased auth model #348, implements OSEP-0006
  • Added/updated docs - specs/sandbox-lifecycle.yml updated with dual auth modes, 401/403 semantics, reserved metadata keys; docs nav updated; console screenshots added
  • Added/updated tests - 6 new server test files, 2 new console unit test files, 1 Playwright e2e spec; all existing tests pass (993 pass, 1 skipped)
  • Security impact considered - API key never reaches browser code; trusted headers are only active when auth.mode = "api_key_and_user" with an explicit reverse proxy in front; missing headers always 401, never anonymous fallback; scope keys (access.owner, access.team) are server-controlled on create and cannot be overridden by the client
  • Backward compatibility considered - api_key_only default preserves all existing behavior; OPEN-SANDBOX-API-KEY path is untouched

…cle (OSEP-0006)

Implements OSEP-0006 Phase 1 (alibaba#348). Closes alibaba#348.

Server — auth / identity
- Config: add AuthConfig (mode: api_key_only|api_key_and_user),
  AuthzConfig (roles, scope keys), ConsoleConfig (optional SPA mount).
  Default is api_key_only — existing deployments are unaffected.
- AuthMiddleware: dual-auth path for console; trusted-header user identity
  set by a reverse proxy. Missing required headers → 401
  MISSING_TRUSTED_IDENTITY; never falls back to anonymous.
- principal.py: Principal dataclass (source, subject, role,
  canonical_owner/team). canonicalize_scoped_value() maps arbitrary
  strings to deterministic Kubernetes label-safe tokens.

Server — authorization and lifecycle
- authorization.py: authorize_action() enforces read_only / operator /
  service_admin role matrix (OSEP-0006 Table 1); 403 on role or
  owner/team scope mismatch.
- lifecycle_helpers.py: authorize_mutating_action() wraps authorize_action
  and emits a mutation_audit entry on denial (403s are always recorded).
  apply_reserved_metadata_for_create() injects access.owner / access.team
  on create; merge_list_scope_from_request() enforces scope on list.
- All mutating routes (create/delete/pause/resume/renew) audit success,
  HTTPException error, 404 not_found, 403 forbidden, and bare Exception
  UNEXPECTED outcomes consistently.
- Optional console SPA static mount in main.py (console.enabled).

Console (console/)
- Standalone React + TypeScript SPA (Vite, Tailwind, dark-default).
- Pages: Sandbox List (filter by state/metadata), Detail (renew, endpoint,
  pause/resume, delete), Create (image, entrypoint, timeout, resources,
  env, metadata).
- API client uses user-auth path — no server API key in browser code.
- Role hint from VITE_UI_ROLE disables mutating buttons for read_only;
  server is always the enforcement point.
- AuthHint renders an explicit misconfiguration banner on 401
  MISSING_TRUSTED_IDENTITY instead of silently retrying.
- Vite dev proxy injects trusted headers from VITE_DEV_IDENTITY_USER /
  VITE_DEV_IDENTITY_ROLES for local development.
- Unit tests (vitest) and Playwright integration tests with reference
  screenshots under docs/public/images/console/.

Spec
- sandbox-lifecycle.yml: document dual auth modes, 401 trusted-header
  semantics, 403 INSUFFICIENT_ROLE / OUT_OF_SCOPE codes, reserved
  metadata keys for ownership scoping.

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
Copilot AI review requested due to automatic review settings May 5, 2026 21:26
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

⚠️ This PR has no labels. Please add one based on the changes.

Changed directories: .gitignore、console、docs.

📋 Recommended labels (based on changed files):

  • documentation ⬅️

Other available labels:

  • bug - Something isn't working
  • dependencies - Pull requests that update a dependency file
  • feature - New feature or request
  • packages - Changes for package, image and configuration

💡 Tip: Use feature for new functionality or improvements, bug for fixes.

cc @divyamagrawal06

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e28332362f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/opensandbox_server/middleware/authorization.py
Comment thread server/opensandbox_server/main.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements Phase 1 of OSEP-0006 by adding a React-based developer console on top of the existing sandbox lifecycle API, while introducing a config-gated dual-auth/authz path for browser users alongside the existing API-key workflow.

Changes:

  • Adds server-side auth/authz primitives for trusted-header users, including roles, owner/team scoping, and audit-style lifecycle helpers.
  • Adds a new console/ SPA for listing, viewing, creating, renewing, pausing/resuming, deleting sandboxes, and fetching endpoints.
  • Updates API spec, sample config, and server/console tests to cover the new rollout path.

Reviewed changes

Copilot reviewed 43 out of 50 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
specs/sandbox-lifecycle.yml Documents dual auth modes and updated lifecycle auth semantics.
server/tests/test_routes_renew_expiration.py Updates renew-expiration route tests for authz-aware flow.
server/tests/test_routes_pause_resume.py Updates pause/resume route tests for prefetch/authz behavior.
server/tests/test_routes_endpoint_behavior.py Adjusts endpoint route tests for authz-backed sandbox lookup.
server/tests/test_routes_create_delete.py Refreshes create/delete route tests and extension validation coverage.
server/tests/test_routes_authorization.py Adds end-to-end route authorization tests for read_only/operator users.
server/tests/test_principal.py Adds unit tests for principal construction and role resolution.
server/tests/test_lifecycle_helpers.py Tests lifecycle helper scoping and forbidden-action logging.
server/tests/test_helpers.py Adds a reusable minimal sandbox test fixture.
server/tests/test_config.py Adds console mount-path validation tests.
server/tests/test_authorization.py Adds unit tests for role matrix and scope checks.
server/tests/test_auth_trusted_header.py Adds trusted-header auth middleware tests.
server/opensandbox_server/middleware/principal.py Introduces principal model, canonical scope values, and role derivation.
server/opensandbox_server/middleware/authorization.py Adds lifecycle authorization and scope enforcement helpers.
server/opensandbox_server/middleware/auth.py Extends auth middleware for API key + trusted-header user mode.
server/opensandbox_server/main.py Mounts the built console SPA when enabled.
server/opensandbox_server/examples/example.config.toml Shows example auth/authz/console configuration.
server/opensandbox_server/config.py Adds auth/authz/console config models and constants.
server/opensandbox_server/api/lifecycle.py Wires authz, scoping, and audit logging into lifecycle routes.
server/opensandbox_server/api/lifecycle_helpers.py Adds shared helpers for principal lookup, scope merge, metadata injection, and audit logs.
docs/.vitepress/config.mts Tweaks docs site presentation config.
console/vitest.config.ts Configures console unit testing.
console/vite.config.ts Configures console base path and dev proxy/header injection.
console/tsconfig.node.json Adds TS config for Vite/Vitest tooling files.
console/tsconfig.json Adds TS config for console source.
console/tests/vitest-setup.ts Sets up jest-dom for Vitest.
console/tests/unit/role.test.ts Tests console role helper behavior.
console/tests/unit/client.test.ts Tests console API client parsing and error handling.
console/tests/playwright.config.example.ts Adds Playwright config for console e2e runs.
console/tests/e2e/console.integration.spec.ts Adds browser-flow tests and screenshot generation.
console/tailwind.config.js Adds Tailwind theme config for the console.
console/src/vite-env.d.ts Adds Vite env typings for the console.
console/src/tailwind.css Adds Tailwind CSS entrypoint.
console/src/pages/ListPage.tsx Implements sandbox list/filter UI.
console/src/pages/DetailPage.tsx Implements sandbox detail and action UI.
console/src/pages/CreatePage.tsx Implements sandbox creation form UI.
console/src/main.tsx Boots the console app and router basename handling.
console/src/components/AuthHint.tsx Adds shared auth and error hint components.
console/src/App.tsx Adds app shell, nav, theme toggle, and routes.
console/src/api/role.ts Adds UI role parsing helpers.
console/src/api/client.ts Adds browser client for lifecycle API calls.
console/postcss.config.js Adds PostCSS/Tailwind pipeline config.
console/package.json Defines console scripts and dependencies.
console/index.html Adds the console HTML entrypoint.
.gitignore Ignores console build and test artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/opensandbox_server/middleware/auth.py
Comment thread console/src/api/role.ts
Comment thread console/vite.config.ts Outdated
Comment thread console/vite.config.ts
Comment thread console/src/api/client.ts Outdated
Comment thread console/tests/e2e/console.integration.spec.ts
Comment thread console/src/App.tsx Outdated
Comment thread server/opensandbox_server/main.py
Comment thread console/src/pages/ListPage.tsx Outdated
Comment thread console/src/pages/DetailPage.tsx
…t-side routing

Snapshot authz gap: snapshot routes were not covered by the OSEP-0006 role
matrix, allowing read_only principals to call create/delete snapshot when
auth.mode=api_key_and_user. Add CREATE_SNAPSHOT and DELETE_SNAPSHOT as
operator-only actions; LIST_SNAPSHOTS and GET_SNAPSHOT as read_only-allowed.
Wire authorize_action / authorize_mutating_action + full mutation audit
logging (success/error/UNEXPECTED) into all four snapshot handlers.
create_snapshot also enforces sandbox owner/team scope, matching the pattern
used for delete/pause/resume/renew.

SPA fallback: StaticFiles(html=True) serves index.html for directory paths
but returns 404 for unknown sub-paths, breaking BrowserRouter routes like
/console/sandboxes/:id on direct load or refresh. Replace with
_SPAStaticFiles which catches 404 from the parent and falls back to
index.html, the standard pattern for single-page apps on Starlette.

Update test_routes_snapshots to patch lifecycle.sandbox_service in
create_snapshot tests now that the handler calls get_sandbox for scope
resolution.

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
- Remove undocumented CONSOLE=1 env injection from CreatePage; it made
  console-created sandboxes behave differently from API requests and risked
  colliding with existing env vars in user workloads
- Fix read-only banner to reference VITE_UI_ROLE (the actual UI hint var)
  instead of VITE_DEV_IDENTITY_ROLES (the dev proxy identity header var)
- Fix vite.config.ts proxy key: try VITE_API_PREFIX first so the proxy and
  the browser client both key off the same env var when the API prefix is
  customized; fall back to VITE_API_BASE_PATH then /v1
- Replace external raw.githubusercontent.com logo img with an inline SVG so
  the console works in offline and CSP-restricted deployments
- Disable 'Next' pagination button when data.pagination.hasNextPage is false
  instead of only while a request is in-flight
- Add accessible sr-only label to the port input in DetailPage
- Replace hardcoded 127.0.0.1:8080 in the 500 fallback message with a
  generic prompt to check server and proxy logs
- Log a warning when console.enabled = true but console/dist is not found
  so operators get an actionable message instead of a silent 404

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
@divyamagrawal06
Copy link
Copy Markdown
Contributor Author

Resolved copilot and codex comments. Would appreciate a review @jwx0925.

Comment thread server/opensandbox_server/api/lifecycle.py Outdated
Comment thread server/opensandbox_server/api/lifecycle.py Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4624e1a8ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/opensandbox_server/api/lifecycle.py Outdated
Comment thread server/opensandbox_server/middleware/authorization.py
Comment thread server/opensandbox_server/api/lifecycle.py Outdated
Comment thread console/src/api/role.ts
…-restore path

- authorize_snapshot_scope helper resolves snapshot's source sandbox and
  raises OUT_OF_SCOPE (403) for user-scoped principals whose sandbox is
  outside their owner/team scope or has been deleted
- create_sandbox: when snapshotId is provided, scope-check the source
  sandbox before delegating to the service layer
- get_snapshot / delete_snapshot: scope-check after snapshot fetch;
  delete also logs a forbidden audit event on rejection
- list_snapshots: if sandboxId filter provided, scope-check that sandbox;
  otherwise inject access_owner/access_team into the SQL query so only
  the caller's own snapshots are returned
- persist access_owner/access_team on SnapshotRecord at create time;
  SQLite migration adds columns to existing databases transparently

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
…ole from server

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38157e908f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/opensandbox_server/api/lifecycle_helpers.py Outdated
Comment thread server/opensandbox_server/api/lifecycle.py Outdated
…box deletion

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
@divyamagrawal06
Copy link
Copy Markdown
Contributor Author

Resolved @jwx0925

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 33b035837d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/opensandbox_server/api/lifecycle.py Outdated
Comment thread server/opensandbox_server/api/lifecycle.py Outdated
Comment thread server/opensandbox_server/api/lifecycle_helpers.py Outdated
Comment thread server/opensandbox_server/middleware/auth.py Outdated
…, tighten fallback and console bypass

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf82d6b56f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/opensandbox_server/services/snapshot_service.py Outdated
…results

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
Resolve conflicts in lifecycle routes (keep OSEP auth/async handlers) and sandbox-lifecycle spec (pool mode + dual-auth docs).

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
Upstream main workflow updates are picked up at PR merge time; reverting avoids OAuth workflow-scope requirement on fork push.

Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
@divyamagrawal06 divyamagrawal06 requested a review from jwx0925 May 22, 2026 08:45
@divyamagrawal06
Copy link
Copy Markdown
Contributor Author

Fixed conflicts.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

last_transition_at,
created_at,
updated_at
FROM snapshots
WHERE id = ?

P1 Badge Include snapshot scope columns in repository reads

These SELECT projections omit access_owner/access_team, so _row_to_record reconstructs snapshots with None scope even when the DB row has ownership metadata. That breaks the new scope model: snapshot_service.get_snapshot() feeds these records into authorize_snapshot_scope, which then treats scoped snapshots as legacy and can allow GET/DELETE /snapshots/{id} after source sandbox deletion because it cannot verify owner/team. Add the scope columns to both get() and list() read queries.


last_transition_at=now,
),
created_at=record.created_at,
updated_at=now,
)

P2 Badge Preserve scope metadata when marking snapshots deleting

The delete-state transition rebuilds SnapshotRecord without copying access_owner and access_team, and update_if_state persists that replacement row. As a result, any snapshot entering Deleting loses its tenant scope metadata; if runtime deletion later errors and the record remains, subsequent authz paths see it as unscoped/legacy instead of owner-bound. Carry forward both access fields when constructing deleting_record.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/publish-components.yml
Comment thread console/src/pages/ListPage.tsx Outdated
Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add developer console for sandbox operations with phased auth model

3 participants