- Complete schema for all 6 governance namespaces - ID generation patterns and examples - Index key patterns for efficient queries - TypeScript interfaces for all objects - Query pattern examples with code - Storage implementation for KV, D1, and Redis 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
17 KiB
17 KiB
BlackRoad OS — KV Schema Reference
Version: 1.0 Last Updated: 2025-11-30 Storage: Cloudflare KV / D1 / Railway Redis
Overview
This document defines the key-value schema for the BlackRoad OS governance layer. All governance objects are stored as JSON values with human-readable keys.
Design Principles
- Keys are human-readable — Debugging at 2am shouldn't require a decoder ring
- Values are JSON — Structured, versionable, extensible
- Prefixes define namespace —
policy:,ledger:,agent:,intent:,claim:,delegation: - Timestamps are ISO 8601 — Always UTC
- IDs use format
{type}-{YYYYMMDD}-{short-hash}— Sortable, unique, traceable
ID Generation
All IDs follow the pattern: {type}-{YYYYMMDD}-{short-hash}
function generateId(type: string): string {
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
const hash = crypto.randomUUID().slice(0, 6);
return `${type}-${date}-${hash}`;
}
// Examples:
// pol-20251130-a1b2c3 (policy)
// evt-20251130-x7y8z9 (event)
// int-20251130-m4n5o6 (intent)
// clm-20251130-p1q2r3 (claim)
// del-20251130-s4t5u6 (delegation)
Namespace: POLICY
Policies define what is allowed, denied, required, or transformed.
Key Pattern
policy:{scope}:{policy_id}
Examples
policy:email.send:pol-20251130-a1b2c3
policy:finance.charge:pol-20251130-b2c3d4
policy:infra.deploy:pol-20251130-c3d4e5
policy:core.safety:pol-20251130-d4e5f6
Schema
interface Policy {
policy_id: string; // pol-YYYYMMDD-xxxxxx
scope: string; // e.g., "email.send", "finance.*"
name: string; // Human-readable name
description?: string; // What this policy does
rules: PolicyRule[]; // Ordered by priority (highest first)
active: boolean; // Is this policy in effect?
created_at: string; // ISO 8601
updated_at: string; // ISO 8601
created_by: string; // user:* or agent:*
}
interface PolicyRule {
rule_id: string; // Unique within policy
condition: string; // Expression to evaluate
action: 'allow' | 'deny' | 'require_human_approval' | 'transform';
priority: number; // Higher wins (0-1000)
reason?: string; // Why this rule exists
transform_fn?: string; // For 'transform' action
}
Example Value
{
"policy_id": "pol-20251130-a1b2c3",
"scope": "email.send",
"name": "External Email Approval",
"description": "Require human approval for emails to external domains",
"rules": [
{
"rule_id": "r1",
"condition": "recipient.domain NOT IN approved_domains",
"action": "require_human_approval",
"priority": 100,
"reason": "Prevent accidental external disclosure"
},
{
"rule_id": "r2",
"condition": "true",
"action": "allow",
"priority": 0
}
],
"active": true,
"created_at": "2025-11-30T12:00:00Z",
"updated_at": "2025-11-30T12:00:00Z",
"created_by": "user:alexa"
}
Index Keys
| Purpose | Key | Value |
|---|---|---|
| All active policies | policy:active |
["pol-xxx", "pol-yyy"] |
| Policies by scope | policy:scope:{scope} |
["pol-xxx"] |
| Policy lookup by ID | policy:id:{policy_id} |
Full policy object |
Namespace: LEDGER
Immutable audit trail of all governance-relevant actions.
Key Pattern
ledger:{intent_id}:{event_id}
Examples
ledger:int-20251130-x1y2z3:evt-20251130-000001
ledger:int-20251130-x1y2z3:evt-20251130-000002
ledger:int-20251130-a4b5c6:evt-20251130-000003
Schema
interface LedgerEvent {
event_id: string; // evt-YYYYMMDD-NNNNNN (sequential within day)
intent_id: string; // Parent intent
timestamp: string; // ISO 8601 UTC
agent_id: string; // Who performed this action
tool: string; // gmail, drive, stripe, etc.
action: string; // search, create, send, charge, etc.
inputs_hash: string; // SHA-256 of inputs
outputs_hash: string; // SHA-256 of outputs
policy_decision: {
result: 'allowed' | 'denied' | 'pending_approval' | 'transformed';
policy_id?: string; // Which policy was applied
rule_matched?: string; // Which rule triggered
};
metadata?: Record<string, any>;
notes?: string; // Human-readable context
}
Example Value
{
"event_id": "evt-20251130-000001",
"intent_id": "int-20251130-x1y2z3",
"timestamp": "2025-11-30T14:32:01.123Z",
"agent_id": "cece.governor.v1",
"tool": "gmail",
"action": "draft",
"inputs_hash": "sha256:abc123def456...",
"outputs_hash": "sha256:789ghi012jkl...",
"policy_decision": {
"result": "allowed",
"policy_id": "pol-20251130-a1b2c3",
"rule_matched": "r2"
},
"metadata": {
"recipient_count": 1,
"has_attachments": false
},
"notes": "Draft created for investor follow-up"
}
Index Keys
| Purpose | Key | Value |
|---|---|---|
| Events by date | ledger:by_date:{YYYY-MM-DD} |
["evt-xxx", "evt-yyy"] |
| Events by agent | ledger:by_agent:{agent_id} |
["evt-xxx"] |
| Events by tool | ledger:by_tool:{tool} |
["evt-xxx"] |
| Events by intent | ledger:by_intent:{intent_id} |
["evt-xxx", "evt-yyy"] |
| Daily counter | ledger:counter:{YYYY-MM-DD} |
42 (for sequential IDs) |
Namespace: AGENT
Registry of all agents in the system.
Key Pattern
agent:{agent_id}
Examples
agent:cece.governor.v1
agent:email.handler.v1
agent:code.writer.v1
agent:data.analyst.v1
Schema
interface Agent {
agent_id: string; // Canonical identifier
name: string; // Human-readable name
description: string; // What this agent does
class: 'lucidia' | 'worker' | 'system' | 'integration';
capabilities: string[]; // Actions this agent can perform
policies_required: string[]; // Policy IDs this agent must respect
owner: string; // user:*, org:*, or blackroad.system
status: 'active' | 'disabled' | 'pending';
config?: Record<string, any>;
created_at: string;
updated_at: string;
last_seen?: string; // Last activity timestamp
}
Example Value
{
"agent_id": "cece.governor.v1",
"name": "Cece",
"description": "Primary governance and orchestration agent for BlackRoad OS",
"class": "lucidia",
"capabilities": [
"intent.create",
"intent.plan",
"tool.invoke",
"policy.evaluate",
"ledger.write",
"agent.coordinate"
],
"policies_required": ["pol-core-safety", "pol-core-audit"],
"owner": "blackroad.system",
"status": "active",
"config": {
"require_human_approval_for": ["finance.*", "email.send_external"],
"default_mode": "operator"
},
"created_at": "2025-11-30T00:00:00Z",
"updated_at": "2025-11-30T12:00:00Z",
"last_seen": "2025-11-30T14:32:01Z"
}
Index Keys
| Purpose | Key | Value |
|---|---|---|
| Active agents | agent:active |
["cece.governor.v1", ...] |
| Agents by class | agent:by_class:{class} |
["cece.governor.v1"] |
| Agents by owner | agent:by_owner:{owner} |
["agent-id-1", ...] |
Namespace: INTENT
Tasks, goals, and workflow state.
Key Pattern
intent:{intent_id}
Examples
intent:int-20251130-x1y2z3
intent:int-20251130-a4b5c6
Schema
interface Intent {
intent_id: string; // int-YYYYMMDD-xxxxxx
actor: string; // Who requested this (user:* or agent:*)
goal: string; // Natural language description
context?: Record<string, any>; // Key details, constraints, references
priority: 'low' | 'normal' | 'high' | 'critical';
status: 'proposed' | 'in_planning' | 'executing' | 'completed' | 'blocked' | 'cancelled';
plan?: IntentStep[]; // Ordered steps with statuses
assigned_agent?: string; // Primary agent handling this
parent_intent_id?: string; // For sub-tasks
created_at: string;
updated_at: string;
completed_at?: string;
blocked_reason?: string;
}
interface IntentStep {
step: number;
action: string; // e.g., "drive.search", "notion.create_page"
status: 'planned' | 'in_progress' | 'completed' | 'failed' | 'skipped';
result?: any;
error?: string;
}
Example Value
{
"intent_id": "int-20251130-x1y2z3",
"actor": "user:alexa",
"goal": "Find investor docs, summarize, create Notion page, draft follow-up email",
"context": {
"search_terms": ["investor", "deck", "pitch"],
"urgency": "end of day"
},
"priority": "high",
"status": "executing",
"plan": [
{"step": 1, "action": "drive.search", "status": "completed"},
{"step": 2, "action": "drive.get", "status": "completed"},
{"step": 3, "action": "summarize", "status": "completed"},
{"step": 4, "action": "notion.create_page", "status": "in_progress"},
{"step": 5, "action": "gmail.draft", "status": "planned"}
],
"assigned_agent": "cece.governor.v1",
"created_at": "2025-11-30T14:00:00Z",
"updated_at": "2025-11-30T14:32:01Z"
}
Index Keys
| Purpose | Key | Value |
|---|---|---|
| Active intents | intent:active |
["int-xxx", "int-yyy"] |
| Intents by status | intent:by_status:{status} |
["int-xxx"] |
| Intents by actor | intent:by_actor:{actor} |
["int-xxx"] |
| Intents by agent | intent:by_agent:{agent_id} |
["int-xxx"] |
Namespace: CLAIM
Assertions about identity, roles, and relationships.
Key Pattern
claim:{subject}:{claim_id}
Examples
claim:user:alexa:clm-20251130-own001
claim:agent:cece.governor.v1:clm-20251130-auth001
claim:org:blackroad:clm-20251130-reg001
Schema
interface Claim {
claim_id: string; // clm-YYYYMMDD-xxxxxx
subject: string; // Who/what this claim is about
claim_type: string; // ownership, authorization, membership, etc.
object: string; // What the claim relates to
assertion: string; // The actual claim statement
issued_by: string; // Who made this claim
issued_at: string; // ISO 8601
expires_at?: string; // Optional expiration
revoked?: boolean;
revoked_at?: string;
revoked_reason?: string;
evidence?: Record<string, any>; // Supporting data
}
Example Value
{
"claim_id": "clm-20251130-own001",
"subject": "user:alexa",
"claim_type": "ownership",
"object": "org:blackroad",
"assertion": "user:alexa is owner of org:blackroad",
"issued_by": "blackroad.system",
"issued_at": "2025-11-30T00:00:00Z",
"expires_at": null,
"revoked": false,
"evidence": {
"registration_date": "2025-01-01",
"verification_method": "email"
}
}
Index Keys
| Purpose | Key | Value |
|---|---|---|
| Active claims | claim:active |
["clm-xxx", ...] |
| Claims by type | claim:by_type:{type} |
["clm-xxx"] |
| Claims by object | claim:by_object:{object} |
["clm-xxx"] |
| Claims by subject | claim:by_subject:{subject} |
["clm-xxx"] |
Namespace: DELEGATION
Scoped permissions from delegator to delegate.
Key Pattern
delegation:{delegator}:{delegation_id}
Examples
delegation:user:alexa:del-20251130-d001
delegation:blackroad.system:del-20251130-d002
delegation:org:blackroad:del-20251130-d003
Schema
interface Delegation {
delegation_id: string; // del-YYYYMMDD-xxxxxx
delegator: string; // Who is granting permission
delegate: string; // Who is receiving permission
scope: string[]; // What actions are permitted
constraints?: {
require_approval_for?: string[]; // Actions needing explicit approval
valid_from?: string; // Start time
valid_until?: string; // End time
max_uses?: number; // Usage limit
conditions?: string[]; // Additional conditions
};
active: boolean;
created_at: string;
updated_at: string;
revoked_at?: string;
revoked_reason?: string;
uses_count?: number; // How many times used
}
Example Value
{
"delegation_id": "del-20251130-d001",
"delegator": "user:alexa",
"delegate": "agent:cece.governor.v1",
"scope": [
"drive.read",
"drive.search",
"notion.*",
"gmail.draft",
"gmail.read"
],
"constraints": {
"require_approval_for": ["gmail.send", "drive.delete", "drive.share"],
"valid_until": "2026-11-30T00:00:00Z"
},
"active": true,
"created_at": "2025-11-30T00:00:00Z",
"updated_at": "2025-11-30T00:00:00Z",
"uses_count": 47
}
Index Keys
| Purpose | Key | Value |
|---|---|---|
| Active delegations | delegation:active |
["del-xxx", ...] |
| By delegate | delegation:by_delegate:{delegate} |
["del-xxx"] |
| By scope | delegation:by_scope:{scope} |
["del-xxx"] |
| By delegator | delegation:by_delegator:{delegator} |
["del-xxx"] |
Query Patterns
Common Queries
| Use Case | Query Pattern |
|---|---|
| Can Cece send email? | Get delegation:by_delegate:agent:cece.governor.v1 → filter for gmail.send |
| What happened today? | Get ledger:by_date:2025-11-30 |
| Who owns BlackRoad? | Get claim:by_object:org:blackroad → filter claim_type: ownership |
| What policies apply to email? | Get policy:scope:email.* + policy:scope:email.send |
| What's Cece working on? | Get intent:by_agent:cece.governor.v1 → filter status: executing |
| Is this action allowed? | Evaluate policies for scope, check delegations, log decision |
Query Implementation
// Check if agent can perform action
async function canPerform(
agent: string,
action: string,
context: any
): Promise<{allowed: boolean; reason: string; policy?: string}> {
// 1. Get delegations for this agent
const delegations = await kv.get(`delegation:by_delegate:${agent}`);
// 2. Check if action is in scope
const hasScope = delegations.some(d =>
d.scope.some(s => matchesScope(s, action))
);
if (!hasScope) {
return {allowed: false, reason: 'No delegation for this action'};
}
// 3. Check if approval required
const needsApproval = delegations.some(d =>
d.constraints?.require_approval_for?.some(s => matchesScope(s, action))
);
if (needsApproval) {
return {allowed: false, reason: 'Requires human approval', policy: 'delegation'};
}
// 4. Evaluate policies
const policies = await kv.get(`policy:scope:${action.split('.')[0]}.*`);
for (const policy of policies.sort((a,b) => b.priority - a.priority)) {
const result = evaluatePolicy(policy, context);
if (result.matched) {
return {
allowed: result.action === 'allow',
reason: result.reason,
policy: policy.policy_id
};
}
}
// 5. Default allow if no policy matched
return {allowed: true, reason: 'No policy restriction'};
}
Storage Implementation
Cloudflare KV
// Direct key-value storage
const policy = await GOVERNANCE_KV.get('policy:email.send:pol-20251130-a1b2c3', 'json');
// Index management (manual)
const activeIds = await GOVERNANCE_KV.get('policy:active', 'json') || [];
activeIds.push(newPolicyId);
await GOVERNANCE_KV.put('policy:active', JSON.stringify(activeIds));
Cloudflare D1 (SQL)
-- Policies table
CREATE TABLE policies (
policy_id TEXT PRIMARY KEY,
scope TEXT NOT NULL,
name TEXT NOT NULL,
rules JSON NOT NULL,
active BOOLEAN DEFAULT true,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX idx_policies_scope ON policies(scope);
CREATE INDEX idx_policies_active ON policies(active);
-- Ledger events table
CREATE TABLE ledger_events (
event_id TEXT PRIMARY KEY,
intent_id TEXT NOT NULL,
timestamp TEXT NOT NULL,
agent_id TEXT NOT NULL,
tool TEXT NOT NULL,
action TEXT NOT NULL,
inputs_hash TEXT,
outputs_hash TEXT,
policy_decision JSON,
metadata JSON
);
CREATE INDEX idx_ledger_intent ON ledger_events(intent_id);
CREATE INDEX idx_ledger_agent ON ledger_events(agent_id);
CREATE INDEX idx_ledger_date ON ledger_events(date(timestamp));
Railway Redis
# Key-value with JSON
SET policy:email.send:pol-20251130-a1b2c3 '{"policy_id":"pol-20251130-a1b2c3",...}'
# Sets for indexes
SADD policy:active pol-20251130-a1b2c3
SADD policy:scope:email.send pol-20251130-a1b2c3
# Sorted sets for time-based queries
ZADD ledger:by_date:2025-11-30 1732972321 evt-20251130-000001
Migration Notes
KV → D1 Migration
When moving from KV to D1 for better query support:
- Export all KV values as JSON
- Transform to relational schema
- Import to D1 tables
- Update application to use SQL queries
- Keep KV for hot paths (caching)
Version Compatibility
All schemas include implicit versioning via timestamps. Breaking changes should:
- Create new key patterns (e.g.,
policy.v2:) - Migrate existing data
- Deprecate old patterns after transition