feat(codec): add json schemas for policy language and primitives

This commit is contained in:
2026-01-08 14:54:41 -05:00
parent e4943509c9
commit 8a953e641b
4 changed files with 2175 additions and 0 deletions

305
internal/codec/policy.json Normal file
View File

@@ -0,0 +1,305 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucan.xyz/schemas/policy.json",
"title": "UCAN Policy Language",
"description": "Policy statements and selectors for constraining invocation arguments",
"$defs": {
"Selector": {
"title": "Policy Selector",
"description": "jq-inspired selector for navigating IPLD data structures",
"type": "string",
"pattern": "^\\.(([a-zA-Z_][a-zA-Z0-9_]*)|(\\[[^\\]]+\\])|(\\[\\])|(\\[-?\\d+\\])|(\\[\\d*:\\d*\\]))?((\\.[a-zA-Z_][a-zA-Z0-9_]*)|(\\[[^\\]]+\\])|(\\[\\])|(\\[-?\\d+\\])|(\\[\\d*:\\d*\\]))*\\??$",
"examples": [
".",
".foo",
".bar[0]",
".items[-1]",
".data[2:5]",
".optional?"
]
},
"GlobPattern": {
"title": "Glob Pattern",
"description": "Pattern for 'like' operator. * = wildcard, \\* = literal",
"type": "string",
"examples": [
"*@example.com",
"prefix*suffix"
]
},
"EqualityOperator": {
"title": "Equality Operator",
"type": "string",
"enum": [
"==",
"!="
]
},
"InequalityOperator": {
"title": "Inequality Operator",
"type": "string",
"enum": [
">",
">=",
"<",
"<="
]
},
"EqualityStatement": {
"title": "Equality Statement",
"description": "Deep comparison: [operator, selector, value]",
"type": "array",
"prefixItems": [
{
"$ref": "#/$defs/EqualityOperator"
},
{
"$ref": "#/$defs/Selector"
},
{}
],
"minItems": 3,
"maxItems": 3,
"examples": [
[
"==",
".status",
"draft"
],
[
"!=",
".deleted",
true
]
]
},
"InequalityStatement": {
"title": "Inequality Statement",
"description": "Numeric comparison: [operator, selector, number]",
"type": "array",
"prefixItems": [
{
"$ref": "#/$defs/InequalityOperator"
},
{
"$ref": "#/$defs/Selector"
},
{
"type": "number"
}
],
"minItems": 3,
"maxItems": 3,
"examples": [
[
">",
".age",
18
],
[
"<=",
".price",
100.50
]
]
},
"LikeStatement": {
"title": "Like Statement",
"description": "Glob pattern matching: ['like', selector, pattern]",
"type": "array",
"prefixItems": [
{
"const": "like"
},
{
"$ref": "#/$defs/Selector"
},
{
"$ref": "#/$defs/GlobPattern"
}
],
"minItems": 3,
"maxItems": 3,
"examples": [
[
"like",
".email",
"*@example.com"
]
]
},
"NotStatement": {
"title": "Not Statement",
"description": "Logical negation: ['not', statement]",
"type": "array",
"prefixItems": [
{
"const": "not"
},
{
"$ref": "#/$defs/PolicyStatement"
}
],
"minItems": 2,
"maxItems": 2
},
"AndStatement": {
"title": "And Statement",
"description": "Logical AND: ['and', [statements...]]. Empty array = true",
"type": "array",
"prefixItems": [
{
"const": "and"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/PolicyStatement"
}
}
],
"minItems": 2,
"maxItems": 2
},
"OrStatement": {
"title": "Or Statement",
"description": "Logical OR: ['or', [statements...]]. Empty array = true",
"type": "array",
"prefixItems": [
{
"const": "or"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/PolicyStatement"
}
}
],
"minItems": 2,
"maxItems": 2
},
"AllStatement": {
"title": "All Statement",
"description": "Universal quantifier: ['all', selector, statement]",
"type": "array",
"prefixItems": [
{
"const": "all"
},
{
"$ref": "#/$defs/Selector"
},
{
"$ref": "#/$defs/PolicyStatement"
}
],
"minItems": 3,
"maxItems": 3,
"examples": [
[
"all",
".reviewers",
[
"like",
".email",
"*@example.com"
]
]
]
},
"AnyStatement": {
"title": "Any Statement",
"description": "Existential quantifier: ['any', selector, statement]",
"type": "array",
"prefixItems": [
{
"const": "any"
},
{
"$ref": "#/$defs/Selector"
},
{
"$ref": "#/$defs/PolicyStatement"
}
],
"minItems": 3,
"maxItems": 3,
"examples": [
[
"any",
".tags",
[
"==",
".",
"urgent"
]
]
]
},
"PolicyStatement": {
"title": "Policy Statement",
"description": "A single policy predicate expression",
"oneOf": [
{
"$ref": "#/$defs/EqualityStatement"
},
{
"$ref": "#/$defs/InequalityStatement"
},
{
"$ref": "#/$defs/LikeStatement"
},
{
"$ref": "#/$defs/NotStatement"
},
{
"$ref": "#/$defs/AndStatement"
},
{
"$ref": "#/$defs/OrStatement"
},
{
"$ref": "#/$defs/AllStatement"
},
{
"$ref": "#/$defs/AnyStatement"
}
]
},
"Policy": {
"title": "UCAN Policy",
"description": "Array of statements forming implicit AND. Constrains invocation args.",
"type": "array",
"items": {
"$ref": "#/$defs/PolicyStatement"
},
"examples": [
[],
[
[
"==",
".from",
"alice@example.com"
]
],
[
[
"==",
".status",
"draft"
],
[
"all",
".reviewer",
[
"like",
".email",
"*@example.com"
]
]
]
]
}
}
}

View File

@@ -0,0 +1,118 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucan.xyz/schemas/primitives.json",
"title": "UCAN Primitive Types",
"description": "Core primitive types used across all UCAN specifications",
"$defs": {
"DID": {
"title": "Decentralized Identifier",
"description": "A W3C Decentralized Identifier (DID) string",
"type": "string",
"pattern": "^did:[a-z0-9]+:[a-zA-Z0-9._%-]+(:[a-zA-Z0-9._%-]+)*([/?#].*)?$",
"examples": [
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
"did:web:example.com"
]
},
"CID": {
"title": "Content Identifier",
"description": "IPLD CIDv1 with DAG-CBOR codec and SHA-256 multihash (base58btc)",
"type": "object",
"properties": {
"/": {
"type": "string",
"pattern": "^zdpu[a-km-zA-HJ-NP-Z1-9]+$"
}
},
"required": [
"/"
],
"additionalProperties": false
},
"Bytes": {
"title": "Binary Data",
"description": "Binary data in DAG-JSON format (base64 encoded)",
"type": "object",
"properties": {
"/": {
"type": "object",
"properties": {
"bytes": {
"type": "string",
"contentEncoding": "base64"
}
},
"required": [
"bytes"
],
"additionalProperties": false
}
},
"required": [
"/"
],
"additionalProperties": false
},
"Timestamp": {
"title": "Unix Timestamp",
"description": "Unix timestamp in seconds (53-bit integer for JS compatibility)",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"NullableTimestamp": {
"title": "Nullable Timestamp",
"description": "Timestamp or null for non-expiring tokens",
"oneOf": [
{
"$ref": "#/$defs/Timestamp"
},
{
"type": "null"
}
]
},
"VarsigHeader": {
"title": "Varsig Header",
"description": "Variable signature header with algorithm metadata",
"allOf": [
{
"$ref": "#/$defs/Bytes"
}
]
},
"Signature": {
"title": "Cryptographic Signature",
"description": "Raw signature bytes",
"allOf": [
{
"$ref": "#/$defs/Bytes"
}
]
},
"Command": {
"title": "UCAN Command",
"description": "Slash-delimited path describing an action (lowercase, starts with /)",
"type": "string",
"pattern": "^/([a-z0-9_\\u00C0-\\u024F]+(/[a-z0-9_\\u00C0-\\u024F]+)*)?$",
"examples": [
"/",
"/crud/create",
"/msg/send",
"/ucan/revoke"
]
},
"Metadata": {
"title": "Metadata",
"description": "Arbitrary metadata map",
"type": "object",
"additionalProperties": true
},
"Arguments": {
"title": "Command Arguments",
"description": "Map of command arguments",
"type": "object",
"additionalProperties": true
}
}
}

File diff suppressed because it is too large Load Diff

592
src/ucan.ts Normal file
View File

@@ -0,0 +1,592 @@
/**
* UCAN Complete TypeScript Type Definitions
* User-Controlled Authorization Network v1.0.0-rc.1
*
* Comprehensive types for Tokens, Delegation, and Invocation specifications
*/
// =============================================================================
// PRIMITIVES
// =============================================================================
/**
* Decentralized Identifier (DID) string
* @see https://www.w3.org/TR/did-core/
*/
export type DID = `did:${string}:${string}`;
/**
* IPLD Content Identifier in DAG-JSON format
* CIDv1 with DAG-CBOR codec and SHA-256 multihash, base58btc encoded
*/
export interface CID {
"/": string;
}
/**
* Binary data in DAG-JSON format (base64 encoded)
*/
export interface Bytes {
"/": {
bytes: string;
};
}
/**
* Unix timestamp in seconds (53-bit integer for JS compatibility)
* Range: -9007199254740991 to 9007199254740991
*/
export type Timestamp = number;
/**
* Nullable timestamp - null indicates non-expiring
*/
export type NullableTimestamp = Timestamp | null;
/**
* Varsig v1 header containing cryptographic algorithm metadata
*/
export type VarsigHeader = Bytes;
/**
* Raw signature bytes
*/
export type Signature = Bytes;
// =============================================================================
// COMMANDS
// =============================================================================
/**
* UCAN Command - slash-delimited path describing the action
* Must be lowercase, start with '/', no trailing slash
*/
export type Command =
| "/"
| `/${string}`
| `/crud/${CRUDAction}`
| `/msg/${MessageAction}`
| `/ucan/${UCANAction}`
| `/wasm/${WasmAction}`;
export type CRUDAction = "create" | "read" | "update" | "delete" | "mutate";
export type MessageAction = "send" | "receive";
export type UCANAction = "revoke";
export type WasmAction = "run";
// =============================================================================
// POLICY LANGUAGE
// =============================================================================
/**
* jq-inspired selector for navigating IPLD data
* @example "." | ".foo" | ".bar[0]" | ".items[-1]" | ".optional?"
*/
export type Selector = string;
/**
* Glob pattern for 'like' operator
* Use * for wildcard, \* for literal asterisk
*/
export type GlobPattern = string;
// Policy Operators
export type EqualityOperator = "==" | "!=";
export type InequalityOperator = ">" | ">=" | "<" | "<=";
export type ConnectiveOperator = "and" | "or";
export type QuantifierOperator = "all" | "any";
// Policy Statements
export type EqualityStatement = [EqualityOperator, Selector, unknown];
export type InequalityStatement = [InequalityOperator, Selector, number];
export type LikeStatement = ["like", Selector, GlobPattern];
export type NotStatement = ["not", PolicyStatement];
export type AndStatement = ["and", PolicyStatement[]];
export type OrStatement = ["or", PolicyStatement[]];
export type AllStatement = ["all", Selector, PolicyStatement];
export type AnyStatement = ["any", Selector, PolicyStatement];
/**
* Union of all policy statement types
*/
export type PolicyStatement =
| EqualityStatement
| InequalityStatement
| LikeStatement
| NotStatement
| AndStatement
| OrStatement
| AllStatement
| AnyStatement;
/**
* UCAN Policy - array of statements forming implicit AND
*/
export type Policy = PolicyStatement[];
// =============================================================================
// METADATA & ARGUMENTS
// =============================================================================
/**
* Arbitrary metadata map
*/
export type Metadata = Record<string, unknown>;
/**
* Command arguments - shape defined by command type
*/
export type Arguments = Record<string, unknown>;
// =============================================================================
// CAPABILITY
// =============================================================================
/**
* UCAN Capability - the semantically-relevant claim of a delegation
*/
export interface Capability {
/** Subject DID or null for powerline delegation */
sub: DID | null;
/** Command being delegated */
cmd: Command;
/** Policy constraints on invocation arguments */
pol: Policy;
}
// =============================================================================
// DELEGATION
// =============================================================================
/**
* UCAN Delegation Payload (ucan/dlg@1.0.0-rc.1)
*/
export interface DelegationPayload {
/** Issuer DID - the delegator */
iss: DID;
/** Audience DID - the delegate */
aud: DID;
/** Subject DID or null for powerline */
sub: DID | null;
/** Command being delegated */
cmd: Command;
/** Policy constraints */
pol: Policy;
/** Random nonce for unique CID */
nonce: Bytes;
/** Optional metadata */
meta?: Metadata;
/** Not-before timestamp (optional) */
nbf?: Timestamp;
/** Expiration timestamp or null */
exp: NullableTimestamp;
}
/**
* Delegation signature payload structure
*/
export interface DelegationSigPayload {
/** Varsig header */
h: VarsigHeader;
/** Delegation payload with version tag */
"ucan/dlg@1.0.0-rc.1": DelegationPayload;
}
/**
* Complete UCAN Delegation envelope
*/
export type Delegation = [Signature, DelegationSigPayload];
// =============================================================================
// INVOCATION
// =============================================================================
/**
* Proof chain - ordered CIDs of delegations from Subject to Invoker
*/
export type ProofChain = CID[];
/**
* UCAN Invocation Payload (ucan/inv@1.0.0-rc.1)
*/
export interface InvocationPayload {
/** Issuer DID - the invoker */
iss: DID;
/** Subject DID being invoked */
sub: DID;
/** Optional audience DID if executor differs from subject */
aud?: DID;
/** Command to execute */
cmd: Command;
/** Command arguments */
args: Arguments;
/** Proof chain of delegations */
prf: ProofChain;
/** Optional metadata */
meta?: Metadata;
/** Optional nonce for non-idempotent invocations */
nonce?: Bytes;
/** Expiration timestamp */
exp: NullableTimestamp;
/** Optional issuance timestamp */
iat?: Timestamp;
/** Optional CID of Receipt that enqueued this task */
cause?: CID;
}
/**
* Invocation signature payload structure
*/
export interface InvocationSigPayload {
/** Varsig header */
h: VarsigHeader;
/** Invocation payload with version tag */
"ucan/inv@1.0.0-rc.1": InvocationPayload;
}
/**
* Complete UCAN Invocation envelope
*/
export type Invocation = [Signature, InvocationSigPayload];
// =============================================================================
// TASK
// =============================================================================
/**
* UCAN Task - subset of Invocation fields uniquely determining work
* Task ID is the CID of these fields
*/
export interface Task {
/** Subject DID */
sub: DID;
/** Command to execute */
cmd: Command;
/** Command arguments */
args: Arguments;
/** Nonce for uniqueness */
nonce: Bytes;
}
// =============================================================================
// RECEIPT
// =============================================================================
/**
* Successful execution result
*/
export interface SuccessResult<T = unknown> {
ok: T;
}
/**
* Failed execution result
*/
export interface ErrorResult<E = unknown> {
err: E;
}
/**
* Execution outcome
*/
export type ExecutionResult<T = unknown, E = unknown> = SuccessResult<T> | ErrorResult<E>;
/**
* UCAN Receipt Payload - execution result
*/
export interface ReceiptPayload<T = unknown, E = unknown> {
/** Executor DID */
iss: DID;
/** CID of executed Invocation */
ran: CID;
/** Execution result */
out: ExecutionResult<T, E>;
/** Effects - CIDs of Tasks to enqueue */
fx?: CID[];
/** Optional metadata */
meta?: Metadata;
/** Issuance timestamp */
iat?: Timestamp;
}
// =============================================================================
// REVOCATION
// =============================================================================
/**
* Revocation arguments
*/
export interface RevocationArgs {
/** CID of delegation to revoke */
ucan: CID;
}
/**
* UCAN Revocation Payload
*/
export interface RevocationPayload {
/** Revoker DID - must be issuer in delegation chain */
iss: DID;
/** Subject of delegation being revoked */
sub: DID;
/** Revocation command */
cmd: "/ucan/revoke";
/** Revocation arguments */
args: RevocationArgs;
/** Proof chain */
prf: ProofChain;
/** Nonce */
nonce: Bytes;
/** Expiration */
exp: NullableTimestamp;
}
// =============================================================================
// ENVELOPE
// =============================================================================
/**
* Generic UCAN Envelope format
*/
export type UCANEnvelope<P extends object> = [
Signature,
{
h: VarsigHeader;
} & P
];
// =============================================================================
// CRYPTOGRAPHY
// =============================================================================
/**
* Supported signature algorithms
*/
export type CryptoAlgorithm = "Ed25519" | "P-256" | "secp256k1";
/**
* Supported hash algorithms
*/
export type HashAlgorithm = "sha2-256";
// =============================================================================
// VALIDATION
// =============================================================================
/**
* Validation error codes
*/
export type ValidationErrorCode =
| "EXPIRED"
| "NOT_YET_VALID"
| "INVALID_SIGNATURE"
| "PRINCIPAL_MISALIGNMENT"
| "POLICY_VIOLATION"
| "REVOKED"
| "INVALID_PROOF_CHAIN"
| "UNKNOWN_COMMAND"
| "MALFORMED_TOKEN";
/**
* Validation error structure
*/
export interface ValidationError {
code: ValidationErrorCode;
message: string;
details?: Record<string, unknown>;
}
/**
* Successful validation result
*/
export interface ValidationSuccess {
valid: true;
capability: Capability;
}
/**
* Failed validation result
*/
export interface ValidationFailure {
valid: false;
error: ValidationError;
}
/**
* Validation result union
*/
export type ValidationResult = ValidationSuccess | ValidationFailure;
// =============================================================================
// ROLES
// =============================================================================
/**
* UCAN Agent Roles
*/
export interface Roles {
/** General class of entities interacting with UCAN */
Agent: DID;
/** Principal delegated to in current UCAN (aud field) */
Audience: DID;
/** Agent that performs invocation action */
Executor: DID;
/** Principal requesting execution */
Invoker: DID;
/** Principal of current UCAN (iss field) */
Issuer: DID;
/** Subject controlling external resource */
Owner: DID;
/** Agent identified by DID */
Principal: DID;
/** Issuer in proof chain that revokes */
Revoker: DID;
/** Principal whose authority is delegated */
Subject: DID;
/** Agent interpreting UCAN validity */
Validator: DID;
}
// =============================================================================
// COMMON COMMANDS (for convenience)
// =============================================================================
/**
* Standard CRUD command arguments
*/
export namespace CRUDCommands {
export interface CreateArgs {
uri: string;
payload: unknown;
headers?: Record<string, string>;
}
export interface ReadArgs {
uri: string;
query?: Record<string, string>;
}
export interface UpdateArgs {
uri: string;
payload: unknown;
headers?: Record<string, string>;
}
export interface DeleteArgs {
uri: string;
}
}
/**
* Standard messaging command arguments
*/
export namespace MessageCommands {
export interface SendArgs {
from: string;
to: string[];
subject?: string;
body: string;
cc?: string[];
bcc?: string[];
}
export interface ReceiveArgs {
mailbox: string;
since?: Timestamp;
limit?: number;
}
}
/**
* WebAssembly execution command arguments
*/
export namespace WasmCommands {
export interface RunArgs {
/** Wasm module as data URI or CID */
mod: string | CID;
/** Function to execute */
fun: string;
/** Function parameters */
params: unknown[];
}
}
// =============================================================================
// TYPE GUARDS
// =============================================================================
export function isDID(value: unknown): value is DID {
return typeof value === "string" && value.startsWith("did:");
}
export function isCID(value: unknown): value is CID {
return (
typeof value === "object" &&
value !== null &&
"/" in value &&
typeof (value as CID)["/"] === "string"
);
}
export function isBytes(value: unknown): value is Bytes {
return (
typeof value === "object" &&
value !== null &&
"/" in value &&
typeof (value as Bytes)["/"] === "object" &&
"bytes" in (value as Bytes)["/"]
);
}
export function isSuccessResult<T>(result: ExecutionResult<T>): result is SuccessResult<T> {
return "ok" in result;
}
export function isErrorResult<E>(result: ExecutionResult<unknown, E>): result is ErrorResult<E> {
return "err" in result;
}
export function isDelegation(token: Delegation | Invocation): token is Delegation {
return "ucan/dlg@1.0.0-rc.1" in token[1];
}
export function isInvocation(token: Delegation | Invocation): token is Invocation {
return "ucan/inv@1.0.0-rc.1" in token[1];
}
// =============================================================================
// UTILITY TYPES
// =============================================================================
/**
* Extract the payload type from a UCAN envelope
*/
export type ExtractPayload<E extends UCANEnvelope<object>> =
E extends UCANEnvelope<infer P> ? P : never;
/**
* Create a typed invocation for a specific command
*/
export type TypedInvocation<C extends Command, A extends Arguments> = [
Signature,
{
h: VarsigHeader;
"ucan/inv@1.0.0-rc.1": Omit<InvocationPayload, "cmd" | "args"> & {
cmd: C;
args: A;
};
}
];
/**
* Create a typed delegation for a specific command
*/
export type TypedDelegation<C extends Command> = [
Signature,
{
h: VarsigHeader;
"ucan/dlg@1.0.0-rc.1": Omit<DelegationPayload, "cmd"> & {
cmd: C;
};
}
];