feat(knowledge): bind domain handoff to signer snapshot
This commit is contained in:
parent
323ceb58c7
commit
5054d4620f
4 changed files with 187 additions and 1 deletions
|
|
@ -1,8 +1,15 @@
|
|||
import { createHash, verify as verifySignature } from 'node:crypto';
|
||||
import {
|
||||
assertRegisteredTrustedManifestSigner,
|
||||
resolveTrustedManifestSigner,
|
||||
type TrustedManifestSigner,
|
||||
type TrustedSignerLookup,
|
||||
} from './trusted-signer-registry.js';
|
||||
import {
|
||||
assertLoadedTrustedSignerSnapshot,
|
||||
type TrustedSignerSnapshotReceipt,
|
||||
type TrustedSignerSnapshotResult,
|
||||
} from './trusted-signer-snapshot.js';
|
||||
|
||||
const MANIFEST_SCHEMA = 'gh-aios.domain-manifest/v1' as const;
|
||||
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
||||
|
|
@ -57,6 +64,11 @@ export interface DomainAccessHandoff {
|
|||
sessionCapability: DomainSessionCapability;
|
||||
}
|
||||
|
||||
export interface DomainAccessHandoffVerification {
|
||||
handoff: DomainAccessHandoff;
|
||||
trustSource: Readonly<TrustedSignerSnapshotReceipt>;
|
||||
}
|
||||
|
||||
export interface DomainAccessEvidence {
|
||||
accountVerified: boolean;
|
||||
connectionReceipt?: DomainConnectionReceipt;
|
||||
|
|
@ -185,6 +197,38 @@ export function parseDomainAccessHandoff(
|
|||
return handoff;
|
||||
}
|
||||
|
||||
export function verifyDomainAccessHandoffFromSnapshot(
|
||||
input: unknown,
|
||||
expectedDomainId: string,
|
||||
expectedNodeId: string,
|
||||
signerLookup: Omit<TrustedSignerLookup, 'domainId'>,
|
||||
snapshot: TrustedSignerSnapshotResult,
|
||||
now = Date.now(),
|
||||
): DomainAccessHandoffVerification {
|
||||
try {
|
||||
const loaded = assertLoadedTrustedSignerSnapshot(snapshot);
|
||||
const registry = loaded.registry;
|
||||
const source = loaded.receipt;
|
||||
if (!registry
|
||||
|| source.status === 'UNAVAILABLE'
|
||||
|| source.sourceCommit !== registry.source.sourceCommit
|
||||
|| source.registryVersion !== registry.version
|
||||
|| source.signerCount !== registry.signers.length) invalidHandoff();
|
||||
const trustedSigner = resolveTrustedManifestSigner(registry, {
|
||||
domainId: expectedDomainId,
|
||||
repositoryId: signerLookup.repositoryId,
|
||||
signerId: signerLookup.signerId,
|
||||
});
|
||||
if (!trustedSigner) invalidHandoff();
|
||||
return Object.freeze({
|
||||
handoff: parseDomainAccessHandoff(input, expectedDomainId, expectedNodeId, trustedSigner, now),
|
||||
trustSource: source,
|
||||
});
|
||||
} catch {
|
||||
invalidHandoff();
|
||||
}
|
||||
}
|
||||
|
||||
export function evaluateDomainAccess(evidence: DomainAccessEvidence, now = Date.now()): DomainAccessStatus {
|
||||
const blockers: string[] = [];
|
||||
if (!evidence.accountVerified || !evidence.nodeId) blockers.push('account_node_identity_missing');
|
||||
|
|
|
|||
Loading…
Reference in a new issue