feat(contracts): @insignia/iios-contracts — scope, enums, platform ports, CloudEvents, errors

Verbatim IiosPlatformPorts from the bottom-up atlas; orgId/appId required scope
(KG-02); PolicyDeniedError for fail-closed. Vitest as workspace test runner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 20:59:34 +05:30
parent c00d038df2
commit fbea291609
12 changed files with 1248 additions and 3 deletions
+12
View File
@@ -0,0 +1,12 @@
/**
* Versioned command envelope (reused from support-service's command pattern).
* Every externally-visible mutation carries a schema name + numeric version +
* an idempotency key. Kept dependency-free here (a type); the service defines a
* class-validator DTO that conforms to it.
*/
export interface CommandEnvelope<P = Record<string, unknown>> {
schemaName: string;
schemaVersion: number;
idempotencyKey: string;
payload: P;
}
+58
View File
@@ -0,0 +1,58 @@
/**
* Kernel enums, lifted verbatim from the Bottom-Up Evolution Atlas §10 DDL.
* Exposed as const arrays + derived union types so they are usable both as
* compile-time types and for runtime validation.
*/
export const ACTOR_KINDS = ['HUMAN', 'AI', 'SERVICE', 'BOT', 'UNKNOWN'] as const;
export type ActorKind = (typeof ACTOR_KINDS)[number];
export const HANDLE_KINDS = [
'PORTAL_USER',
'EMAIL',
'PHONE',
'WHATSAPP',
'SLACK',
'MATTERMOST',
'TELEGRAM',
'EXTERNAL_VISITOR',
] as const;
export type HandleKind = (typeof HANDLE_KINDS)[number];
export const INTERACTION_KINDS = [
'MESSAGE',
'EMAIL',
'SYSTEM_NOTICE',
'INBOX_WORK',
'SUPPORT_CASE',
'MEETING_REQUEST',
'DIGEST',
'SUMMARY',
'NOTIFICATION',
] as const;
export type InteractionKind = (typeof INTERACTION_KINDS)[number];
export const MESSAGE_PART_KINDS = [
'TEXT',
'HTML',
'MARKDOWN',
'MEDIA_REF',
'FILE_REF',
'VOICE_REF',
'LOCATION',
'STRUCTURED_JSON',
] as const;
export type MessagePartKind = (typeof MESSAGE_PART_KINDS)[number];
export const DELIVERY_STATES = [
'RECEIVED',
'NORMALIZED',
'POLICY_HELD',
'STORED',
'DELIVERED',
'READ',
'FAILED',
'DELETED_LOCAL',
'REDACTED',
] as const;
export type DeliveryState = (typeof DELIVERY_STATES)[number];
+22
View File
@@ -0,0 +1,22 @@
/** Typed kernel errors carrying a stable machine code. */
export class IiosError extends Error {
constructor(
public readonly code: string,
message: string,
) {
super(message);
this.name = 'IiosError';
}
}
/**
* Thrown when a platform port denies, times out, or is unavailable for a
* privileged action. The kernel fails CLOSED: nothing is written. (Bottom-Up
* hard gate; Critics KG-03.)
*/
export class PolicyDeniedError extends IiosError {
constructor(reason = 'policy denied') {
super('POLICY_DENIED', reason);
this.name = 'PolicyDeniedError';
}
}
+44
View File
@@ -0,0 +1,44 @@
/**
* CloudEvents envelope + the IIOS event catalog (Data Model §05, Ref Arch §09).
*
* Every durable state change writes the business row AND an outbox event in the
* same transaction. The `insignia` block carries correlation/causation/scope and
* the idempotency key; payloads reference content rather than embedding raw PII.
*/
export type DataClass = 'internal' | 'restricted' | 'confidential' | 'regulated';
export interface CloudEvent<T = unknown> {
specversion: '1.0';
id: string;
type: string;
source: string;
subject?: string;
time: string;
datacontenttype?: 'application/json';
traceparent?: string;
insignia: {
correlationId?: string;
causationId?: string;
scopeSnapshotId?: string;
policyDecisionId?: string;
consentReceiptRef?: string;
contextBundleId?: string;
idempotencyKey: string;
dataClass?: DataClass;
};
data: T;
}
/** Canonical event names (the subset relevant up to P4; others added per phase). */
export const IIOS_EVENTS = {
rawReceived: 'com.insignia.iios.raw.received.v1',
interactionNormalized: 'com.insignia.iios.interaction.normalized.v1',
messagePersisted: 'com.insignia.iios.message.persisted.v1',
inboxItemCreated: 'com.insignia.iios.inbox.item.created.v1',
ticketCreated: 'com.insignia.iios.support.ticket.created.v1',
notificationQueued: 'com.insignia.iios.notification.queued.v1',
deliveryAttempted: 'com.insignia.iios.delivery.attempted.v1',
} as const;
export type IiosEventType = (typeof IIOS_EVENTS)[keyof typeof IIOS_EVENTS];
+35
View File
@@ -0,0 +1,35 @@
import { describe, it, expect } from 'vitest';
import {
IIOS_EVENTS,
PolicyDeniedError,
ACTOR_KINDS,
type ScopeVector,
type IiosPlatformPorts,
} from './index';
describe('iios-contracts public entrypoint', () => {
it('exports the event catalog', () => {
expect(IIOS_EVENTS.interactionNormalized).toBe('com.insignia.iios.interaction.normalized.v1');
});
it('PolicyDeniedError carries the stable POLICY_DENIED code', () => {
const err = new PolicyDeniedError('opa denied');
expect(err.code).toBe('POLICY_DENIED');
expect(err).toBeInstanceOf(Error);
});
it('actor kinds include the four real principals + UNKNOWN', () => {
expect(ACTOR_KINDS).toContain('HUMAN');
expect(ACTOR_KINDS).toContain('AI');
expect(ACTOR_KINDS).toContain('SERVICE');
});
it('types are usable (compile-time check)', () => {
const scope: ScopeVector = { orgId: 'org_1', appId: 'portal-demo' };
const ports: Pick<IiosPlatformPorts, 'opa'> = {
opa: { decide: async () => ({ decisionRef: 'd', allow: true, obligations: [], ttlSeconds: 60 }) },
};
expect(scope.appId).toBe('portal-demo');
expect(typeof ports.opa.decide).toBe('function');
});
});
+6
View File
@@ -0,0 +1,6 @@
export * from './scope';
export * from './enums';
export * from './ports';
export * from './events';
export * from './commands';
export * from './errors';
+57
View File
@@ -0,0 +1,57 @@
/**
* Platform ports contract — copied verbatim from the Bottom-Up Evolution Atlas
* "Platform ports contract" TS block (the single source of truth).
*
* These describe the platform-governance fabric (Session/OPA/CMP/MDM/CRRE/SAS/
* Capability) that OTHER teams own and that does not exist yet. IIOS consumes
* them through this interface; in dev/test it binds to deterministic fakes
* (@insignia/iios-testkit). In prod it binds to the real services. The kernel
* never builds these — it only depends on the contract.
*/
export interface PlatformPrincipal {
principalRef: string;
accountSubject?: string;
canonicalEntityId?: string;
orgId: string;
appId: string;
tenantId?: string;
workspaceId?: string;
assurance: 'anonymous' | 'authenticated' | 'mfa' | 'service';
delegationChain?: string[];
}
export interface PolicyDecision {
decisionRef: string;
allow: boolean;
obligations: Array<{
kind: 'MASK' | 'REVIEW' | 'AUDIT' | 'DENY_OUTBOUND' | 'REDACT';
target?: string;
reason: string;
}>;
ttlSeconds: number;
}
export interface ContextDecisionBundle {
bundleRef: string;
scopeHash: string;
resourceSet: Array<{ type: string; id: string; relation?: string }>;
constraints: Array<{ kind: string; value: unknown; sourceRule: string }>;
routeHints?: Array<{ routeBindingId: string; mode: 'SIMULATION_ONLY' | 'MANUAL' | 'AUTOMATIC' }>;
}
export interface SourceHandleResolution {
canonicalEntityId?: string;
confidence: number;
status: string;
}
export interface IiosPlatformPorts {
session: { verify(token: string): Promise<PlatformPrincipal> };
opa: { decide(input: unknown): Promise<PolicyDecision> };
cmp: { checkPurpose(input: unknown): Promise<{ receiptRef?: string; status: 'ALLOW' | 'DENY' | 'NOT_REQUIRED' }> };
mdm: { resolveSourceHandle(input: unknown): Promise<SourceHandleResolution> };
crre: { resolve(input: unknown): Promise<ContextDecisionBundle> };
sas: { tokenizeParts(parts: unknown[]): Promise<unknown[]> };
capability: { invoke(input: unknown): Promise<{ providerRef: string; outcome: string }> };
}
+16
View File
@@ -0,0 +1,16 @@
/**
* The six scope vectors (Reference Arch §08, Data Model §02).
*
* `orgId` + `appId` are REQUIRED. Nullable scope is treated as global access —
* a security hole the critics flag as a kill gate (Critics KG-02). The remaining
* dimensions are optional and carry no kernel logic yet; later phases use them.
*/
export interface ScopeVector {
orgId: string;
appId: string;
buId?: string;
tenantId?: string;
workspaceId?: string;
projectId?: string;
userScopeId?: string;
}