feat(0pt): canonical-fact machine route + renumber migration to 022 #484
+4
-1
@@ -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$/,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user