export interface DomainAccessEvidence { accountVerified: boolean; connectionReceipt?: { domainId: string; manifestDigest: string; nodeId: string; state: 'online' }; domainId: string; manifest?: { digest: string; domainId: string; verified: boolean }; nodeId: string; sessionCapability?: { domainId: string; expiresAt: number; nodeId: string; scopes: string[] }; } export interface DomainAccessStatus { blockers: string[]; domainId: string; localWorkspaceAllowed: true; nodeId: string; runtimeReady: boolean; stage: 'login-required' | 'identity-verified' | 'runtime-ready'; } export function evaluateDomainAccess(evidence: DomainAccessEvidence, now = Date.now()): DomainAccessStatus { const blockers: string[] = []; if (!evidence.accountVerified || !evidence.nodeId) blockers.push('account_node_identity_missing'); if (!evidence.manifest?.verified || evidence.manifest.domainId !== evidence.domainId || !evidence.manifest.digest) { blockers.push('verified_domain_manifest_missing'); } const capability = evidence.sessionCapability; if (!capability || capability.domainId !== evidence.domainId || capability.nodeId !== evidence.nodeId || capability.expiresAt <= now || !capability.scopes.includes('domain:enter')) { blockers.push('scoped_session_capability_missing'); } const receipt = evidence.connectionReceipt; if (!receipt || receipt.state !== 'online' || receipt.domainId !== evidence.domainId || receipt.nodeId !== evidence.nodeId || receipt.manifestDigest !== evidence.manifest?.digest) { blockers.push('matching_connection_receipt_missing'); } const runtimeReady = blockers.length === 0; return { blockers, domainId: evidence.domainId, localWorkspaceAllowed: true, nodeId: evidence.nodeId, runtimeReady, stage: runtimeReady ? 'runtime-ready' : evidence.accountVerified && Boolean(evidence.nodeId) ? 'identity-verified' : 'login-required', }; }