diff --git a/middleware.ts b/middleware.ts index 1933a57..1af30ef 100644 --- a/middleware.ts +++ b/middleware.ts @@ -4,7 +4,7 @@ * Customer calls use verified Zitadel bearer identity and receive immutable * x-0pt-verified-* headers. Explicit machine routes may instead use the * shared X-Internal-Token convention; mutation handlers verify it again for - * defence in depth. + * defence in depth. The canonical-fact write is one such explicit machine route. */ import { NextResponse, type NextRequest } from 'next/server'; import { assertAuthConfigSafe, resolveAuthContext } from '@/src/auth/context'; @@ -26,6 +26,9 @@ const EXEMPT_PATTERNS: RegExp[] = [ /** Routes that may be reached with a service token instead of end-user bearer auth. */ const INTERNAL_SERVICE_ROUTES: RegExp[] = [ /^\/api\/0pt\/quality\/bridge-run$/, + // Acquisition engine -> 0pt canonical observation write (machine-to-machine, + // same X-Internal-Token class as the quality bridge; not a blanket bypass). + /^\/api\/0pt\/canonical\/facts$/, /^\/api\/0pt\/internal\/evidence-outbox\/dispatch$/, /^\/api\/0pt\/internal\/er\/candidates$/, /^\/api\/0pt\/search\/definitions$/, diff --git a/sql/020_canonical_facts.sql b/sql/022_canonical_facts.sql similarity index 100% rename from sql/020_canonical_facts.sql rename to sql/022_canonical_facts.sql diff --git a/src/repositories/canonical-fact-repository.ts b/src/repositories/canonical-fact-repository.ts index 6b4f7b6..4e22ed7 100644 --- a/src/repositories/canonical-fact-repository.ts +++ b/src/repositories/canonical-fact-repository.ts @@ -1,7 +1,7 @@ /** * src/repositories/canonical-fact-repository.ts * ============================================= - * Storage seam for 0pt canonical entities/facts/relations (sql/020). + * Storage seam for 0pt canonical entities/facts/relations (sql/022). * Same split as source-repository.ts: one interface, an in-memory * implementation (dev/test) and a Postgres implementation (production), * chosen once via resolveStoreMode. diff --git a/src/services/canonical-fact-service.ts b/src/services/canonical-fact-service.ts index 96bd620..e636227 100644 --- a/src/services/canonical-fact-service.ts +++ b/src/services/canonical-fact-service.ts @@ -1,7 +1,7 @@ /** * src/services/canonical-fact-service.ts * ====================================== - * 0pt's canonical structured-observation write/read surface (sql/020). + * 0pt's canonical structured-observation write/read surface (sql/022). * * Ownership boundary: 0pt stores normalized OBSERVED facts (what a source * said, when, from which artifact). L0RE owns epistemic propositions and diff --git a/src/validation/0pt-schemas.ts b/src/validation/0pt-schemas.ts index acfc881..148a359 100644 --- a/src/validation/0pt-schemas.ts +++ b/src/validation/0pt-schemas.ts @@ -264,7 +264,7 @@ export const activateSourceSchema = z.object({ actor_id: z.string().min(1).optional(), }); -/** POST /api/0pt/canonical/facts — canonical structured observation write (sql/020). */ +/** POST /api/0pt/canonical/facts — canonical structured observation write (sql/022). */ const canonicalEntityInputSchema = z.object({ entity_type: z.string().min(1), identity_namespace: z.string().min(1), diff --git a/tests/auth/middleware-internal-token.test.ts b/tests/auth/middleware-internal-token.test.ts index ec57c4f..a7bad48 100644 --- a/tests/auth/middleware-internal-token.test.ts +++ b/tests/auth/middleware-internal-token.test.ts @@ -45,6 +45,13 @@ describe('middleware.ts: internal-token path for /api/0pt/quality/bridge-run', ( expect(res.status).toBe(200); }); + it('a valid X-Internal-Token lets the acquisition engine write canonical facts', async () => { + const res = await middleware( + req('/api/0pt/canonical/facts', { 'x-internal-token': 'engine-shared-secret' }), + ); + expect(res.status).toBe(200); + }); + it('an invalid X-Internal-Token still falls through to the ordinary Bearer requirement (401, not a bypass)', async () => { const res = await middleware(req('/api/0pt/quality/bridge-run', { 'x-internal-token': 'wrong-secret' })); expect(res.status).toBe(401); diff --git a/tests/p0/canonical-facts.test.ts b/tests/p0/canonical-facts.test.ts index 0e3e7f4..9c9b854 100644 --- a/tests/p0/canonical-facts.test.ts +++ b/tests/p0/canonical-facts.test.ts @@ -1,5 +1,5 @@ /** - * Canonical structured observations (sql/020): idempotent write, conflict + * Canonical structured observations (sql/022): idempotent write, conflict * preservation, tenant isolation and relation identity resolution. */ import { describe, it, expect } from "vitest";