Skip to main content
The Council crate provides governance through proposals (submit/approve/reject), agent overrides (pause/resume/terminate), and a tamper-proof merkle-chained activity log.

Overview

  • Purpose: Governance with cryptographic audit trail
  • LOC: ~450
  • Functions: 6 (submit, decide, proposals, override, activity, activity_log, verify)
  • Endpoints: 6 REST + 1 PubSub
  • Source: crates/council/src/main.rs

Core Concepts

Proposal Structure

struct Proposal {
    id: String,                    // "prop-{uuid}"
    realm_id: String,
    kind: String,                  // "hire_agent", "budget_increase", etc.
    status: ProposalStatus,        // Pending, Approved, Rejected
    title: String,
    payload: Value,                // Arbitrary proposal data
    requested_by: String,
    decided_by: Option<String>,
    decision_note: Option<String>,
    decided_at: Option<String>,
    created_at: String,
}

Activity Entry (Audit Log)

struct ActivityEntry {
    id: String,                    // "act-{uuid}"
    realm_id: String,
    actor_kind: ActorKind,         // Agent, Human, System
    actor_id: String,
    action: String,                // "proposal_submitted", "override_pause", etc.
    entity_type: String,           // "proposal", "agent", "directive", etc.
    entity_id: String,
    details: Option<Value>,
    hash: String,                  // SHA-256 hash
    prev_hash: String,             // Previous entry's hash
    timestamp: String,
}

Merkle Chain

Entry 0: hash = SHA256("0000..." + action + entity + timestamp)
Entry 1: hash = SHA256(Entry0.hash + action + entity + timestamp)
Entry 2: hash = SHA256(Entry1.hash + action + entity + timestamp)
Any tampering breaks the chain and is detectable via council::verify.

Functions

council::submit

Submit a proposal for approval.
realmId
string
required
Realm identifier
kind
string
required
Proposal type (free-form string)
title
string
required
Proposal title
payload
object
Proposal-specific data
requestedBy
string
required
Agent or user submitting proposal
Example:
iii.trigger("council::submit", json!({
    "realmId": "r-1",
    "kind": "hire_agent",
    "title": "Hire Research Agent",
    "payload": {
        "role": "research-analyst",
        "template": "agents/researcher.yaml",
        "estimatedMonthlyCost": 5000
    },
    "requestedBy": "agent-lead"
})).await?;

// Returns:
// {
//   "id": "prop-abc123",
//   "status": "Pending",
//   "createdAt": "2024-03-09T10:00:00Z",
//   ...
// }
REST Endpoint:
POST /api/council/proposals
Content-Type: application/json

{
  "realmId": "r-1",
  "kind": "hire_agent",
  "title": "Hire Research Agent",
  "requestedBy": "agent-lead"
}
Common Proposal Types:
  • hire_agent - Request to spawn a new agent
  • budget_increase - Request additional budget
  • directive_create - Request realm-level directive
  • tool_grant - Request access to restricted tool
  • realm_config - Request realm configuration change

council::decide

Approve or reject a proposal.
realmId
string
required
Realm identifier
id
string
required
Proposal ID
approved
boolean
required
true = approve, false = reject
decidedBy
string
required
Human or system making the decision
note
string
Optional decision rationale
Example:
// Approve proposal
iii.trigger("council::decide", json!({
    "realmId": "r-1",
    "id": "prop-abc123",
    "approved": true,
    "decidedBy": "admin",
    "note": "Approved. Research capacity is critical for Q2 goals."
})).await?;

// Reject proposal
iii.trigger("council::decide", json!({
    "realmId": "r-1",
    "id": "prop-xyz789",
    "approved": false,
    "decidedBy": "admin",
    "note": "Budget constraints. Revisit in Q3."
})).await?;
REST Endpoint:
POST /api/council/proposals/prop-abc123/decide
Content-Type: application/json

{
  "approved": true,
  "decidedBy": "admin",
  "note": "Approved for Q2"
}

council::proposals

List proposals with optional status filter.
realmId
string
required
Realm identifier
status
string
Filter by status: “Pending”, “Approved”, “Rejected”
Example:
// Get pending proposals
let pending = iii.trigger("council::proposals", json!({
    "realmId": "r-1",
    "status": "Pending"
})).await?;

// Returns:
// {
//   "proposals": [ ... ],
//   "count": 3
// }
REST Endpoint:
GET /api/council/proposals/r-1?status=Pending

council::override

Override agent state (pause/resume/terminate).
realmId
string
required
Realm identifier
targetAgentId
string
required
Agent to override
action
string
required
Override action: “pause”, “resume”, “terminate”
operatorId
string
required
Human operator performing override
reason
string
Reason for override
Example:
// Pause agent
iii.trigger("council::override", json!({
    "realmId": "r-1",
    "targetAgentId": "agent-dev-1",
    "action": "pause",
    "operatorId": "admin",
    "reason": "Investigation of billing anomaly"
})).await?;

// Resume agent
iii.trigger("council::override", json!({
    "realmId": "r-1",
    "targetAgentId": "agent-dev-1",
    "action": "resume",
    "operatorId": "admin",
    "reason": "Investigation complete, no issues found"
})).await?;

// Terminate agent
iii.trigger("council::override", json!({
    "realmId": "r-1",
    "targetAgentId": "agent-rogue",
    "action": "terminate",
    "operatorId": "admin",
    "reason": "Unauthorized data access detected"
})).await?;
REST Endpoint:
POST /api/council/override
Content-Type: application/json

{
  "realmId": "r-1",
  "targetAgentId": "agent-dev-1",
  "action": "pause",
  "operatorId": "admin",
  "reason": "Investigation"
}

council::activity

Log an activity entry to the audit trail.
realmId
string
required
Realm identifier
actorKind
string
required
Actor type: “Agent”, “Human”, “System”
actorId
string
required
Actor identifier
action
string
required
Action performed
entityType
string
required
Entity type affected
entityId
string
required
Entity identifier
details
object
Additional context
Example:
iii.trigger("council::activity", json!({
    "realmId": "r-1",
    "actorKind": "Agent",
    "actorId": "agent-dev-1",
    "action": "mission_completed",
    "entityType": "mission",
    "entityId": "msn-xyz789",
    "details": {
        "duration_secs": 3600,
        "cost_cents": 250
    }
})).await?;
PubSub Trigger: Council also listens to council.audit topic:
iii.register_trigger("subscribe", "council::activity", 
    json!({ "topic": "council.audit" }))?;
Hash Computation (council/src/main.rs:42-49):
fn compute_hash(prev_hash: &str, action: &str, entity_id: &str, timestamp: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(prev_hash.as_bytes());
    hasher.update(action.as_bytes());
    hasher.update(entity_id.as_bytes());
    hasher.update(timestamp.as_bytes());
    hex::encode(hasher.finalize())
}

council::activity_log

Retrieve recent activity log entries.
realmId
string
required
Realm identifier
limit
number
default:50
Max entries to return
Example:
let log = iii.trigger("council::activity_log", json!({
    "realmId": "r-1",
    "limit": 100
})).await?;

// Returns:
// {
//   "entries": [
//     {
//       "id": "act-123",
//       "actorKind": "Human",
//       "actorId": "admin",
//       "action": "override_pause",
//       "entityType": "agent",
//       "entityId": "agent-dev-1",
//       "hash": "a1b2c3...",
//       "prevHash": "d4e5f6...",
//       "timestamp": "2024-03-09T10:00:00Z"
//     },
//     ...
//   ],
//   "count": 100
// }
REST Endpoint:
GET /api/council/activity/r-1?limit=100

council::verify

Verify integrity of the activity chain.
realmId
string
required
Realm identifier
Example:
let result = iii.trigger("council::verify", json!({
    "realmId": "r-1"
})).await?;

// Returns:
// {
//   "valid": true,
//   "entryCount": 1523
// }
REST Endpoint:
GET /api/council/verify/r-1
Verification Algorithm (council/src/main.rs:282-319):
let mut valid = true;
let mut expected_prev = "0".repeat(64);  // Genesis hash

for entry in &entries {
    // Check prev_hash matches
    if entry.prev_hash != expected_prev {
        valid = false;
        break;
    }
    
    // Recompute hash
    let computed = compute_hash(&entry.prev_hash, &entry.action, &entry.entity_id, &entry.timestamp);
    if computed != entry.hash {
        valid = false;  // Tampering detected!
        break;
    }
    
    expected_prev = entry.hash.clone();
}
If verification returns valid: false, the audit log has been tampered with. Investigate immediately.

Storage Scopes

  • realm:{realmId}:proposals - Governance proposals
  • realm:{realmId}:activity - Merkle-chained audit log

Events

Council publishes to multiple topics: council.proposal:
{
  "type": "submitted" | "approved" | "rejected",
  "proposalId": "prop-abc123",
  "realmId": "r-1"
}
council.override:
{
  "action": "pause" | "resume" | "terminate",
  "agentId": "agent-dev-1",
  "operatorId": "admin",
  "realmId": "r-1"
}

Use Cases

Agent Governance

Require approval for high-impact agent actions (hiring, budget increases).

Compliance Audit

Cryptographically verifiable audit log for SOC 2, ISO 27001, GDPR compliance.

Emergency Response

Quickly pause or terminate misbehaving agents with full audit trail.

Tamper Detection

Detect any unauthorized modifications to the activity log via hash verification.

Best Practices

1

Define proposal types

Standardize proposal kinds (“hire_agent”, “budget_increase”) for consistency.
2

Require decision notes

Always include a note when approving/rejecting for transparency.
3

Log all significant actions

Call council::activity for mission completion, directive changes, overrides, etc.
4

Verify chain regularly

Run council::verify daily or weekly to detect tampering early.
5

Use overrides sparingly

Document override reasons thoroughly. Overuse indicates systemic issues.
6

Archive old activity

For long-running realms, periodically export and archive old activity entries.
  • Ledger - Budget violations logged to council activity
  • Realm - Each realm has independent governance
  • Directive - Major directive changes require proposals

Build docs developers (and LLMs) love