Skip to content

feat: inject jwt.claims.api_id provenance claim and add REST /fn routes#1364

Open
pyramation wants to merge 1 commit into
mainfrom
feat/api-provenance-context
Open

feat: inject jwt.claims.api_id provenance claim and add REST /fn routes#1364
pyramation wants to merge 1 commit into
mainfrom
feat/api-provenance-context

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

Track B1 of constructive-planning#1149: trusted server-side API provenance + REST function invocation.

Provenance claim (jwt.claims.api_id) — set transaction-locally via pgSettings for every request on an API surface, derived exclusively from the server's hostname → services_public.domainsapi_id resolution; clients can never influence it (headers/body/token payload are ignored):

  • express-context/pg-settings.ts: settings['jwt.claims.api_id'] = api.apiId alongside the existing jwt.claims.* keys.
  • graphql/server/middleware/graphile.ts: same claim added to the shared PostGraphile context (spread into both authenticated and anonymous settings).

This pairs with the parallel DB-side change to the function_invocations INSERT RLS policy (b.api_id = jwt_private.current_api_id()); the api arm depends on this claim being present.

Compute module loader — new computeLoader in express-context resolves function/invocation module metadata (metaschema_modules_public.function_module / function_invocation_module) per database, registered in createDefaultRegistry(). The GraphQL server now passes loaders: createDefaultRegistry() to createContextMiddleware so ctx.useModule(...) works at runtime.

REST routes (graphql/server/middleware/fn.ts, mounted before graphile):

  • POST /fn/:alias — looks up function_api_bindings by (api_id, alias); 404 when no binding, config.rest absent (REST disabled), or method not in config.rest.methods; otherwise inserts the function_invocations row through ctx.withPgClient (request pgSettings incl. the claim, RLS enforced — no superuser/bypass path) and returns 202 { "invocationId": ... }.
  • GET /fn/invocations/:id — RLS-guarded read of invocation status/result; 404 for unknown/inaccessible rows.
  • ajv/JSON-Schema payload validation intentionally not included this pass — explicit TODO hook left before the insert.

Tests

  • packages/express-context/__tests__/pg-settings.test.ts: claim present for anon + authenticated API requests, omitted off API surfaces, token payload cannot override it.
  • graphql/server-test/__tests__/fn-routes.integration.test.ts (+ __fixtures__/seed/compute/*): happy path 202, the insert passing a seeded api-arm RLS policy mirroring the production one, and 404s for missing binding / rest-disabled / disallowed method / unknown or non-uuid invocation id. The fixture policy notes the dependency on the parallel constructive-db policy work.

Note: pnpm lint (eslint 9 vs .eslintrc.json) and 81 server-test failures are pre-existing on the base commit — verified by stashing this branch and re-running; this branch adds 7 new tests, all passing, with no new failures.

Refs constructive-io/constructive-planning#1149

Link to Devin session: https://app.devin.ai/sessions/2fa82d89240d444681d41758548445a6
Requested by: @pyramation

- Set jwt.claims.api_id transaction-locally via pgSettings for every
  request on an API surface, derived from the server-side hostname ->
  services_public.domains -> api_id resolution (never client input)
- Add compute module loader (function/invocation module metadata)
- Add POST /fn/:alias and GET /fn/invocations/:id REST routes resolving
  function_api_bindings by (api_id, alias); 404 on missing binding,
  rest-disabled config, or disallowed method; RLS enforced end to end
- ajv/JSON-Schema payload validation intentionally deferred (TODO hook)

Refs constructive-io/constructive-planning#1149
@pyramation pyramation self-assigned this Jul 12, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

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.

1 participant