Skip to content

feat(oracle): proxy oracle routes to the standalone service and add internal identity endpoints - #1726

Closed
chronoai-kaihuei wants to merge 4 commits into
mainfrom
feat/oracle-upstream-proxy
Closed

chronoai-kaihuei wants to merge 4 commits into
mainfrom
feat/oracle-upstream-proxy

Conversation

@chronoai-kaihuei

Copy link
Copy Markdown
Collaborator

What

First NyxID-side step of moving the oracle into its own Go service (ChronoAIProject/oracle). Nothing changes unless the new variables are set.

  • ORACLE_UPSTREAM_URL: when set, the /api/v1/oracle/* consumer nest and the /api/v1/oracle/worker/* nest become a streaming reverse proxy to that URL (headers allow-listed, X-Forwarded-* added, request and response bodies streamed so 16 MiB results and SSE are not buffered). The literal value hold answers 503 with Retry-After: 10 for the migration window. Unreachable upstream answers 502. The delegated/relay token rejection layers still apply in front of the proxy.
  • ORACLE_INTERNAL_SECRET (64 lowercase hex): enables three endpoints on the internal listener (port 3002), authenticated by a constant-time bearer check, Cache-Control: no-store:
    • POST /api/v1/internal/oracle/introspect runs the real AuthUser credential path on a synthetic request and returns token_class, principal, scope, api_key, memberships and exp; inactive credentials return 200 with active: false and a reason.
    • GET /api/v1/internal/oracle/principals/{user_id} returns user type, liveness and memberships.
    • POST /api/v1/internal/oracle/audit forwards a metadata-only audit event.
  • New error oracle_upstream_unavailable (11017): 502 for an unreachable upstream, 503 under hold.
  • Docs: docs/ENV.md, .env.example, docs/ORACLE_RELAY.md section "Standalone oracle service".

The in-process oracle routes are untouched and remain the default. The billing route inventory test is unchanged.

How to test

cargo test -p nyxid -- internal_oracle oracle_proxy validate_oracle_config oracle_upstream_errors error_codes_unique error_keys

Three tests need MongoDB: set NYXID_TEST_DATABASE_URL to a writable URI (a replica set is not required). 22 tests pass.

Notes for review

  • The proxy reuses the shared state.http_client (10 s connect timeout, no overall timeout) rather than adding a second client.
  • The introspect reason for inactive credentials is derived from the extractor's error text (expired, inactive_user, revoked, else invalid); the Go client only special-cases expired.
  • The consuming service and its wire contract live in ChronoAIProject/oracle (docs/protocol/auth.md).

ORACLE_UPSTREAM_URL is either an http(s) base URL or the literal hold.
ORACLE_INTERNAL_SECRET is 64 lowercase hex characters and is required
whenever the upstream URL is set. Both are validated at startup and
documented in docs/ENV.md and .env.example.
OracleUpstreamUnavailable answers 502 when the standalone oracle service
cannot be reached. OracleUpstreamHold shares the key and code and answers
503 with Retry-After: 10 while ORACLE_UPSTREAM_URL=hold.
With ORACLE_UPSTREAM_URL set, the /api/v1/oracle consumer nest and the
/api/v1/oracle/worker nest become a streaming reverse proxy
(handlers/oracle_proxy.rs). The delegated and relay token rejections on
the parent nest still run first. The in-process routes move into helper
functions and stay the default when the variable is unset; the
oracle_billing_routes! inventory is unchanged.

With ORACLE_INTERNAL_SECRET set, the private listener mounts
POST /api/v1/internal/oracle/introspect, GET .../principals/{user_id}
and POST .../audit (handlers/internal_oracle.rs). Introspection runs the
real AuthUser extractor over synthetic request parts so every credential
class is classified exactly as a live oracle request would be.
@chronoai-kaihuei

Copy link
Copy Markdown
Collaborator Author

Closed: the standalone oracle will not use a NyxID-side proxy cutover.

@chronoai-kaihuei
chronoai-kaihuei deleted the feat/oracle-upstream-proxy branch October 1, 2026 08:22
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

📊 Code coverage

Component Lines Threshold Status Δ vs base
Backend (nyxid) 87.56% 73% ✅ 🔺 +0.02

Gate: line coverage must stay at or above the threshold. Ratchet plan (W21): Backend → 55%, CLI → 50%, Frontend → 30% by quarter end.

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.

2 participants