From 8ceb4d600f20bf6ddf10022f63bbcfcb86887878 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Sat, 10 Jan 2026 16:57:37 -0500 Subject: [PATCH] docs(dwn): remove UCAN_SCHEMA_PROPOSAL.md --- .github/Repo.toml.migrated.20260109_191955 | 4 - docs/UCAN_SCHEMA_PROPOSAL.md | 311 --------------------- example/main.js | 7 +- 3 files changed, 4 insertions(+), 318 deletions(-) delete mode 100644 .github/Repo.toml.migrated.20260109_191955 delete mode 100644 docs/UCAN_SCHEMA_PROPOSAL.md diff --git a/.github/Repo.toml.migrated.20260109_191955 b/.github/Repo.toml.migrated.20260109_191955 deleted file mode 100644 index 6da103c..0000000 --- a/.github/Repo.toml.migrated.20260109_191955 +++ /dev/null @@ -1,4 +0,0 @@ -[scopes] -docs = ["MIGRATION.md", "README.md"] -db = ["db"] -config = [".github"] diff --git a/docs/UCAN_SCHEMA_PROPOSAL.md b/docs/UCAN_SCHEMA_PROPOSAL.md deleted file mode 100644 index b7405ca..0000000 --- a/docs/UCAN_SCHEMA_PROPOSAL.md +++ /dev/null @@ -1,311 +0,0 @@ -# UCAN v1.0.0-rc.1 Schema Proposal - -## Overview - -This document proposes schema changes to migrate from JWT-based UCAN to v1.0.0-rc.1 envelope format. - -## Design Principles - -1. **Single Database** - All data in one SQLite for WASM portability -2. **CID-based Lookup** - Primary key is content identifier (immutable) -3. **Binary Storage** - DAG-CBOR envelopes stored as BLOBs -4. **Indexed Fields** - Extract key fields for efficient queries -5. **DID Ownership** - Foreign key to did_documents for access control - -## Architecture - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ WASM Plugin (Enclave) │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ WebAuthn │ │ DID │ │ UCAN │ │ -│ │ (AuthN) │ │ (Identity) │ │ (AuthZ) │ │ -│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ -│ │ │ │ │ -│ ▼ ▼ ▼ │ -│ ┌──────────────────────────────────────────────────────────┐ │ -│ │ SQLite Database │ │ -│ │ ┌────────────┐ ┌────────────┐ ┌─────────────────────┐ │ │ -│ │ │credentials │ │did_documents│ │ ucan_delegations │ │ │ -│ │ │ │ │ │ │ ucan_invocations │ │ │ -│ │ │ │ │ │ │ ucan_revocations │ │ │ -│ │ └────────────┘ └────────────┘ └─────────────────────┘ │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -## Schema Changes - -### 1. Replace `ucan_tokens` with `ucan_delegations` - -```sql --- DROP TABLE IF EXISTS ucan_tokens; -- Migration step - --- UCAN Delegations: v1.0.0-rc.1 delegation envelopes -CREATE TABLE IF NOT EXISTS ucan_delegations ( - id INTEGER PRIMARY KEY, - did_id INTEGER NOT NULL REFERENCES did_documents(id) ON DELETE CASCADE, - - -- Content Identifier (immutable, unique) - cid TEXT NOT NULL UNIQUE, - - -- Sealed envelope (DAG-CBOR encoded) - envelope BLOB NOT NULL, - - -- Extracted fields for indexing/queries - iss TEXT NOT NULL, -- Issuer DID - aud TEXT NOT NULL, -- Audience DID - sub TEXT, -- Subject DID (null = powerline) - cmd TEXT NOT NULL, -- Command (e.g., "/vault/read") - - -- Policy stored as JSON for inspection (actual evaluation uses envelope) - pol TEXT DEFAULT '[]', -- Policy JSON - - -- Temporal fields - nbf TEXT, -- Not before (ISO8601) - exp TEXT, -- Expiration (ISO8601, null = never) - - -- Metadata - is_root INTEGER NOT NULL DEFAULT 0, -- iss == sub - is_powerline INTEGER NOT NULL DEFAULT 0, -- sub IS NULL - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); - -CREATE INDEX idx_ucan_delegations_cid ON ucan_delegations(cid); -CREATE INDEX idx_ucan_delegations_did_id ON ucan_delegations(did_id); -CREATE INDEX idx_ucan_delegations_iss ON ucan_delegations(iss); -CREATE INDEX idx_ucan_delegations_aud ON ucan_delegations(aud); -CREATE INDEX idx_ucan_delegations_sub ON ucan_delegations(sub); -CREATE INDEX idx_ucan_delegations_cmd ON ucan_delegations(cmd); -CREATE INDEX idx_ucan_delegations_exp ON ucan_delegations(exp); -``` - -### 2. Add `ucan_invocations` Table - -```sql --- UCAN Invocations: v1.0.0-rc.1 invocation envelopes (audit log) -CREATE TABLE IF NOT EXISTS ucan_invocations ( - id INTEGER PRIMARY KEY, - did_id INTEGER NOT NULL REFERENCES did_documents(id) ON DELETE CASCADE, - - -- Content Identifier - cid TEXT NOT NULL UNIQUE, - - -- Sealed envelope (DAG-CBOR encoded) - envelope BLOB NOT NULL, - - -- Extracted fields for indexing - iss TEXT NOT NULL, -- Invoker DID - sub TEXT NOT NULL, -- Subject DID - aud TEXT, -- Executor DID (if different from sub) - cmd TEXT NOT NULL, -- Command invoked - - -- Proof chain (JSON array of delegation CIDs) - prf TEXT NOT NULL DEFAULT '[]', - - -- Temporal - exp TEXT, -- Expiration - iat TEXT, -- Issued at - - -- Execution tracking - executed_at TEXT, -- When actually executed - result_cid TEXT, -- CID of receipt (if executed) - - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); - -CREATE INDEX idx_ucan_invocations_cid ON ucan_invocations(cid); -CREATE INDEX idx_ucan_invocations_iss ON ucan_invocations(iss); -CREATE INDEX idx_ucan_invocations_sub ON ucan_invocations(sub); -CREATE INDEX idx_ucan_invocations_cmd ON ucan_invocations(cmd); -``` - -### 3. Update `ucan_revocations` (Minor Changes) - -```sql --- UCAN Revocations: Track revoked delegations --- (Mostly unchanged, but add invocation reference) -CREATE TABLE IF NOT EXISTS ucan_revocations ( - id INTEGER PRIMARY KEY, - delegation_cid TEXT NOT NULL UNIQUE, -- CID of revoked delegation - revoked_by TEXT NOT NULL, -- Revoker DID - invocation_cid TEXT, -- CID of revocation invocation - reason TEXT, - revoked_at TEXT NOT NULL DEFAULT (datetime('now')), - - FOREIGN KEY (delegation_cid) REFERENCES ucan_delegations(cid) ON DELETE CASCADE -); - -CREATE INDEX idx_ucan_revocations_delegation_cid ON ucan_revocations(delegation_cid); -CREATE INDEX idx_ucan_revocations_revoked_by ON ucan_revocations(revoked_by); -``` - -### 4. Remove `delegations` Table - -The old `delegations` table extracted fields from `ucan_tokens`. In v1.0.0-rc.1, the delegation IS the token. The `ucan_delegations` table replaces both. - -```sql --- DROP TABLE IF EXISTS delegations; -- Migration step -``` - -## Query Examples - -### Get Delegation by CID (for go-ucan Loader) - -```sql --- name: GetDelegationByCID :one -SELECT envelope FROM ucan_delegations WHERE cid = ? LIMIT 1; -``` - -### List Delegations Granted TO a DID (audience) - -```sql --- name: ListDelegationsToAudience :many -SELECT * FROM ucan_delegations -WHERE aud = ? AND (exp IS NULL OR exp > datetime('now')) -ORDER BY created_at DESC; -``` - -### List Delegations Granted BY a DID (issuer) - -```sql --- name: ListDelegationsByIssuer :many -SELECT * FROM ucan_delegations -WHERE iss = ? AND (exp IS NULL OR exp > datetime('now')) -ORDER BY created_at DESC; -``` - -### Find Delegations for a Command - -```sql --- name: ListDelegationsForCommand :many -SELECT * FROM ucan_delegations -WHERE did_id = ? - AND (cmd = ? OR cmd = '/' OR ? LIKE cmd || '/%') - AND (exp IS NULL OR exp > datetime('now')) -ORDER BY created_at DESC; -``` - -### Check if Delegation is Revoked - -```sql --- name: IsDelegationRevoked :one -SELECT EXISTS(SELECT 1 FROM ucan_revocations WHERE delegation_cid = ?) as revoked; -``` - -## Go Integration - -### Delegation Loader Implementation - -```go -// internal/keybase/ucan_loader.go - -package keybase - -import ( - "context" - "fmt" - - "github.com/ipfs/go-cid" - "github.com/ucan-wg/go-ucan/token/delegation" -) - -// DelegationLoader implements delegation.Loader for go-ucan -type DelegationLoader struct { - queries *Queries -} - -func NewDelegationLoader(queries *Queries) *DelegationLoader { - return &DelegationLoader{queries: queries} -} - -// GetDelegation implements delegation.Loader -func (l *DelegationLoader) GetDelegation(c cid.Cid) (*delegation.Token, error) { - ctx := context.Background() - - envelope, err := l.queries.GetDelegationEnvelopeByCID(ctx, c.String()) - if err != nil { - return nil, fmt.Errorf("delegation not found: %s", c) - } - - // Decode DAG-CBOR envelope to delegation token - return delegation.FromSealed(envelope) -} -``` - -### Action Manager Integration - -```go -// internal/keybase/actions_delegation_v2.go - -type DelegationV2Result struct { - CID string `json:"cid"` - Issuer string `json:"iss"` - Audience string `json:"aud"` - Subject string `json:"sub,omitempty"` - Command string `json:"cmd"` - Policy string `json:"pol"` - ExpiresAt string `json:"exp,omitempty"` - IsRoot bool `json:"is_root"` - CreatedAt string `json:"created_at"` -} - -func (am *ActionManager) StoreDelegation(ctx context.Context, sealed []byte, c cid.Cid) (*DelegationV2Result, error) { - // Decode to extract indexed fields - token, err := delegation.FromSealed(sealed) - if err != nil { - return nil, err - } - - // Store in database - result, err := am.kb.queries.CreateDelegationV2(ctx, CreateDelegationV2Params{ - DidID: am.kb.didID, - Cid: c.String(), - Envelope: sealed, - Iss: token.Issuer().String(), - Aud: token.Audience().String(), - Sub: didToNullable(token.Subject()), - Cmd: token.Command().String(), - Pol: policyToJSON(token.Policy()), - Exp: timeToNullable(token.Expiration()), - Nbf: timeToNullable(token.NotBefore()), - IsRoot: boolToInt(token.IsRoot()), - IsPowerline: boolToInt(token.IsPowerline()), - }) - - return delegationV2ToResult(result), nil -} -``` - -## Migration Path - -1. **Create new tables** alongside old ones -2. **Migrate existing data** (if any JWT tokens exist) - - Parse old `raw_token` - - Re-encode as v1.0.0-rc.1 envelope (requires re-signing) - - Or: Mark old tokens as legacy, start fresh with v1.0.0-rc.1 -3. **Update ActionManager** to use new tables -4. **Drop old tables** after migration verified - -## Benefits - -| Aspect | Old (JWT) | New (v1.0.0-rc.1) | -|--------|-----------|-------------------| -| Storage | JSON text | Binary CBOR (smaller) | -| Verification | Parse JWT, verify sig | go-ucan handles it | -| Proof Chain | JSON array in token | Separate CID references | -| Policy | `capabilities` JSON | Structured `pol` field | -| Interop | Non-standard | Spec-compliant | - -## Conclusion - -Integrating UCAN v1.0.0-rc.1 into the keybase schema: - -1. **Maintains single-database portability** for WASM plugin -2. **Leverages existing infrastructure** (SQLC, ActionManager) -3. **Enables foreign key relationships** with DID documents -4. **Provides efficient queries** via indexed fields -5. **Supports go-ucan integration** via `delegation.Loader` diff --git a/example/main.js b/example/main.js index 97b527f..567235e 100644 --- a/example/main.js +++ b/example/main.js @@ -6,8 +6,8 @@ let currentResource = 'accounts'; let currentAction = 'list'; const RESOURCE_ACTIONS = { - accounts: ['list', 'get'], - enclaves: ['list', 'get', 'rotate', 'archive', 'delete'], + accounts: ['list', 'get', 'sign'], + enclaves: ['list', 'get', 'sign', 'rotate', 'archive', 'delete'], credentials: ['list', 'get'], sessions: ['list', 'revoke'], grants: ['list', 'revoke'], @@ -17,7 +17,7 @@ const RESOURCE_ACTIONS = { services: ['list', 'get', 'get_by_id'], }; -const ACTIONS_REQUIRING_SUBJECT = ['get', 'revoke', 'delete', 'verify', 'rotate', 'archive', 'list_received', 'list_command', 'get_by_id']; +const ACTIONS_REQUIRING_SUBJECT = ['get', 'revoke', 'delete', 'verify', 'rotate', 'archive', 'list_received', 'list_command', 'get_by_id', 'sign']; function log(card, level, message, data = null) { const el = document.getElementById(`log-${card}`); @@ -322,6 +322,7 @@ function updateSubjectRow() { list_received: 'Audience DID', list_command: 'Command (e.g., msg/send)', get_by_id: 'Service ID (number)', + sign: currentResource === 'enclaves' ? 'enclave_id:data_to_sign' : 'Data to sign (text)', }; subjectInput.placeholder = placeholders[currentAction] || 'Subject';