Add web4-governance extension for AI governance with R6 workflow

Adds lightweight AI governance extension with:
- R6 workflow formalism (Rules, Role, Request, Reference, Resource → Result)
- Session start/end hooks for session tracking and audit initialization
- Before/after tool call hooks for R6 request logging and audit trails
- /audit command for generating governance reports
- Hash-linked audit chain for verifiable provenance

The extension provides transparent accountability for AI agent actions
without impeding productivity, using a policy-based approach that
can be customized per team or organization.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
dp-web4 2026-01-27 12:27:14 -08:00
parent 3fe4b2595a
commit aa15d95986
9 changed files with 1026 additions and 0 deletions

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Web4 Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,179 @@
# Web4 Governance Extension for Moltbot
Lightweight AI governance with R6 workflow formalism and audit trails.
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
## Overview
This extension adds structured governance to Moltbot sessions:
- **R6 Workflow** - Every tool call follows a formal intent→action→result flow
- **Audit Trail** - Verifiable chain of actions with provenance
- **Session Identity** - Software-bound tokens for session tracking
No external dependencies. No network calls. Just structured, auditable AI actions.
## Installation
The extension is bundled with Moltbot. To enable it, configure in your Moltbot config:
```json
{
"plugins": {
"web4-governance": {
"enabled": true,
"auditLevel": "standard",
"showR6Status": true,
"actionBudget": null
}
}
}
```
## What It Does
### Every Tool Call Gets an R6 Record
The R6 framework captures structured intent:
```
R6 = Rules + Role + Request + Reference + Resource → Result
```
| Component | What It Captures |
|-----------|------------------|
| **Rules** | Preferences and constraints |
| **Role** | Session identity, action index |
| **Request** | Tool name, category, target |
| **Reference** | Chain position, previous R6 |
| **Resource** | (Optional) Estimated cost |
| **Result** | Status, output hash |
### Audit Trail with Provenance
Each action creates an audit record linked to its R6 request:
```json
{
"record_id": "audit:f8e9a1b2",
"r6_request_id": "r6:f8e9a1b2",
"tool": "Edit",
"category": "write",
"target": "src/main.rs",
"result": {
"status": "success",
"output_hash": "a1b2c3d4..."
},
"provenance": {
"session_id": "abc123",
"action_index": 47,
"prev_record_hash": "..."
}
}
```
Records form a hash-linked chain, enabling verification.
### Session Identity
Sessions get a software-bound token:
```
web4:session:a1b2c3d4
```
This is **not** hardware-bound (no TPM/Secure Enclave). Trust interpretation is up to the relying party. For hardware-bound identity, see [Hardbound](https://github.com/dp-web4/hardbound).
## Commands
| Command | Description |
|---------|-------------|
| `/audit` | Show session audit summary |
| `/audit last 10` | Show last 10 actions |
| `/audit verify` | Verify chain integrity |
| `/audit export` | Export audit log |
## Configuration
Available configuration options:
```json
{
"plugins": {
"web4-governance": {
"auditLevel": "standard",
"showR6Status": true,
"actionBudget": null
}
}
}
```
**auditLevel**:
- `minimal` - Just record, no output
- `standard` - Session start message
- `verbose` - Show each R6 request
**showR6Status**: Show session token on session start
**actionBudget**: Maximum number of actions (null = unlimited)
## Files
```
~/.web4/
├── preferences.json # User preferences (optional)
├── sessions/ # Session state
│ └── {session_id}.json
├── audit/ # Audit records
│ └── {session_id}.jsonl
└── r6/ # R6 request logs
└── {date}.jsonl
```
## Why R6?
The R6 framework provides:
1. **Structured Intent** - Every action has documented purpose
2. **Audit Foundation** - Machine-readable action history
3. **Context Preservation** - Reference links maintain history
4. **Trust Basis** - Verifiable record for trust evaluation
5. **Policy Hook** - Rules component enables future enforcement
R6 is observational by default - it records, doesn't block. This makes it safe to deploy without disrupting workflows.
## Web4 Ecosystem
This extension implements a subset of the [Web4 trust infrastructure](https://github.com/dp-web4/web4):
| Concept | This Extension | Full Web4 |
|---------|----------------|-----------|
| Identity | Software token | LCT (hardware-bound) |
| Workflow | R6 framework | R6 + Policy enforcement |
| Audit | Hash-linked chain | Distributed ledger |
| Trust | (Relying party decides) | T3 Trust Tensor |
For enterprise features (hardware binding, team governance, policy enforcement), see [Hardbound](https://github.com/dp-web4/hardbound).
## Contributing
Contributions welcome! This extension is MIT licensed.
Areas for contribution:
- Additional audit visualizations
- R6 analytics and insights
- Integration with external audit systems
- Performance optimizations
## License
MIT License - see [LICENSE](LICENSE)
## Links
- [Web4 Specification](https://github.com/dp-web4/web4)
- [R6 Framework Spec](https://github.com/dp-web4/web4/blob/main/web4-standard/core-spec/r6-framework.md)
- [Hardbound (Enterprise)](https://github.com/dp-web4/hardbound)
- [Moltbot](https://github.com/moltbot/moltbot)

View File

@ -0,0 +1,23 @@
{
"id": "web4-governance",
"channels": [],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"auditLevel": {
"type": "string",
"enum": ["minimal", "standard", "verbose"],
"default": "standard"
},
"showR6Status": {
"type": "boolean",
"default": true
},
"actionBudget": {
"type": ["number", "null"],
"default": null
}
}
}
}

View File

@ -0,0 +1,171 @@
/**
* Web4 Governance Extension for Moltbot
* SPDX-License-Identifier: MIT
* Copyright (c) 2025 Web4 Contributors
*
* Lightweight AI governance with R6 workflow formalism and audit trails.
* https://github.com/dp-web4/web4
*/
import type { MoltbotPluginApi, MoltbotPluginDefinition } from "clawdbot/plugin-sdk";
import { initializeSession, loadSession, saveSession } from "./src/session.js";
import {
createR6Request,
finalizeR6Request,
createAuditRecord,
persistR6Request,
persistAuditRecord,
} from "./src/r6.js";
import { handleAuditCommand } from "./src/audit-command.js";
import type { SessionState } from "./src/types.js";
// Active session store (in-memory, per-process)
const activeSessions = new Map<string, SessionState>();
const plugin: MoltbotPluginDefinition = {
id: "web4-governance",
name: "Web4 Governance",
description:
"Lightweight AI governance with R6 workflow formalism and audit trails. Every tool call becomes a structured request with verifiable provenance.",
version: "1.0.0",
async register(api: MoltbotPluginApi) {
api.logger.info("Web4 Governance extension loaded");
// Register session lifecycle hooks
api.on("session_start", async (event, ctx) => {
const sessionId = event.sessionId || ctx.sessionId;
if (!sessionId) return;
try {
// Try to load existing session, or create new
let session = await loadSession(sessionId);
if (!session) {
session = await initializeSession(sessionId);
// Show status if preference enabled
if (session.preferences.showR6Status && api.logger.info) {
const tokenShort = session.token.token_id.split(":")[2];
api.logger.info(`[Web4] Session ${tokenShort} (software-bound)`);
}
}
// Store in active sessions
activeSessions.set(sessionId, session);
} catch (error) {
api.logger.error(`Failed to initialize Web4 session: ${error}`);
}
});
api.on("session_end", async (event, ctx) => {
const sessionId = event.sessionId || ctx.sessionId;
if (!sessionId) return;
try {
const session = activeSessions.get(sessionId);
if (session) {
// Final save
await saveSession(session);
activeSessions.delete(sessionId);
if (api.logger.info) {
api.logger.info(
`[Web4] Session ended: ${session.action_count} actions audited`,
);
}
}
} catch (error) {
api.logger.error(`Failed to finalize Web4 session: ${error}`);
}
});
// Register tool call audit hooks
api.on("before_tool_call", async (event, ctx) => {
const sessionId = ctx.sessionKey || ctx.agentId;
if (!sessionId) return;
try {
let session = activeSessions.get(sessionId);
if (!session) {
// Session might not be in memory, try to load
session = await loadSession(sessionId);
if (!session) {
// Create session on-the-fly if needed
session = await initializeSession(sessionId);
}
activeSessions.set(sessionId, session);
}
// Create R6 request
const r6 = createR6Request(session, event.toolName, event.params);
// Store R6 temporarily (will be finalized in after_tool_call)
session.r6_requests.push(r6);
// Persist R6 request
await persistR6Request(r6);
} catch (error) {
api.logger.error(`Failed to create R6 request: ${error}`);
}
});
api.on("after_tool_call", async (event, ctx) => {
const sessionId = ctx.sessionKey || ctx.agentId;
if (!sessionId) return;
try {
const session = activeSessions.get(sessionId);
if (!session) return;
// Get the pending R6 request
const pendingR6 = session.r6_requests[session.r6_requests.length - 1];
if (!pendingR6 || pendingR6.result) {
// Already finalized or missing
return;
}
// Finalize R6 with result
const finalizedR6 = finalizeR6Request(pendingR6, event.result, event.error);
session.r6_requests[session.r6_requests.length - 1] = finalizedR6;
// Create audit record
const auditRecord = createAuditRecord(finalizedR6, session);
session.audit_chain.push(auditRecord);
// Increment action count
session.action_count++;
// Persist audit record
await persistAuditRecord(auditRecord, sessionId);
// Save session state
await saveSession(session);
// Check action budget if configured
if (
session.preferences.actionBudget &&
session.action_count >= session.preferences.actionBudget
) {
api.logger.warn(
`[Web4] Action budget reached: ${session.action_count}/${session.preferences.actionBudget}`,
);
}
} catch (error) {
api.logger.error(`Failed to finalize R6 request: ${error}`);
}
});
// Register /audit command
api.registerCommand({
name: "audit",
description: "Show Web4 governance audit summary and reports",
acceptsArgs: true,
requireAuth: true,
handler: handleAuditCommand,
});
api.logger.info("Web4 Governance hooks and commands registered");
},
};
export default plugin;

View File

@ -0,0 +1,26 @@
{
"name": "web4-governance",
"version": "1.0.0",
"description": "Lightweight AI governance with R6 workflow formalism and audit trails for Moltbot",
"type": "module",
"main": "index.ts",
"scripts": {},
"keywords": [
"governance",
"audit",
"trust",
"r6",
"provenance",
"web4"
],
"author": "Web4 Contributors <web4@metalinxx.io>",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"moltbot": "workspace:*"
},
"repository": {
"type": "git",
"url": "https://github.com/dp-web4/web4"
}
}

View File

@ -0,0 +1,220 @@
/**
* Web4 Governance Extension - Audit Command
* SPDX-License-Identifier: MIT
* Copyright (c) 2025 Web4 Contributors
*/
import { createHash } from "node:crypto";
import { readFile, readdir } from "node:fs/promises";
import { join } from "node:path";
import type { PluginCommandHandler } from "clawdbot/plugin-sdk";
import { getWeb4Paths, loadSession } from "./session.js";
import type { AuditRecord, SessionState } from "./types.js";
/**
* Handle /audit command.
*/
export const handleAuditCommand: PluginCommandHandler = async (ctx) => {
const args = ctx.args?.trim() || "";
// Parse command arguments
if (args.startsWith("last ")) {
const count = parseInt(args.slice(5), 10);
return await showLastActions(count);
}
if (args === "verify") {
return await verifyChainIntegrity();
}
if (args === "export") {
return await exportAuditLog();
}
// Default: show session summary
return await showSessionSummary();
};
/**
* Show session summary.
*/
async function showSessionSummary() {
try {
const { sessionDir } = getWeb4Paths();
const sessions = await readdir(sessionDir);
if (sessions.length === 0) {
return { text: "No Web4 governance sessions found." };
}
// Get latest session
const latestSessionFile = sessions.sort().reverse()[0];
const sessionId = latestSessionFile.replace(".json", "");
const session = await loadSession(sessionId);
if (!session) {
return { text: "Failed to load session data." };
}
const tokenShort = session.token.token_id.split(":")[2];
const duration = Date.now() - new Date(session.started_at).getTime();
const durationMin = Math.floor(duration / 60000);
const text = [
"**Web4 Governance Session Summary**",
"",
`Session: ${tokenShort} (software-bound)`,
`Started: ${new Date(session.started_at).toLocaleString()}`,
`Duration: ${durationMin} minutes`,
`Actions: ${session.action_count}`,
`Audit Level: ${session.preferences.auditLevel}`,
"",
"Use `/audit last 10` to see recent actions",
"Use `/audit verify` to verify chain integrity",
"Use `/audit export` to export full audit log",
].join("\n");
return { text };
} catch (error) {
return { text: `Error reading audit data: ${error}` };
}
}
/**
* Show last N actions.
*/
async function showLastActions(count: number) {
try {
const { sessionDir, auditDir } = getWeb4Paths();
const sessions = await readdir(sessionDir);
if (sessions.length === 0) {
return { text: "No audit records found." };
}
// Get latest session
const latestSessionFile = sessions.sort().reverse()[0];
const sessionId = latestSessionFile.replace(".json", "");
// Read audit log
const auditFile = join(auditDir, `${sessionId}.jsonl`);
const auditData = await readFile(auditFile, "utf-8");
const records: AuditRecord[] = auditData
.trim()
.split("\n")
.map((line) => JSON.parse(line));
const lastRecords = records.slice(-count);
const lines = [
`**Last ${lastRecords.length} Actions**`,
"",
];
for (const record of lastRecords) {
const time = new Date(record.timestamp).toLocaleTimeString();
const status = record.result.status === "success" ? "✓" : "✗";
lines.push(
`${status} [${time}] ${record.tool} (${record.category})${record.target ? `${record.target}` : ""}`,
);
}
return { text: lines.join("\n") };
} catch (error) {
return { text: `Error reading audit log: ${error}` };
}
}
/**
* Verify chain integrity.
*/
async function verifyChainIntegrity() {
try {
const { sessionDir, auditDir } = getWeb4Paths();
const sessions = await readdir(sessionDir);
if (sessions.length === 0) {
return { text: "No audit chain to verify." };
}
// Get latest session
const latestSessionFile = sessions.sort().reverse()[0];
const sessionId = latestSessionFile.replace(".json", "");
// Read audit log
const auditFile = join(auditDir, `${sessionId}.jsonl`);
const auditData = await readFile(auditFile, "utf-8");
const records: AuditRecord[] = auditData
.trim()
.split("\n")
.map((line) => JSON.parse(line));
// Verify chain
let isValid = true;
let prevHash: string | undefined;
for (let i = 0; i < records.length; i++) {
const record = records[i];
const expectedPrevHash = prevHash;
const actualPrevHash = record.provenance.prev_record_hash;
if (i === 0) {
if (actualPrevHash !== undefined) {
isValid = false;
break;
}
} else {
if (actualPrevHash !== expectedPrevHash) {
isValid = false;
break;
}
}
// Compute hash for next iteration
prevHash = createHash("sha256")
.update(JSON.stringify(record))
.digest("hex")
.slice(0, 16);
}
if (isValid) {
return {
text: `✓ Audit chain verified: ${records.length} records, integrity intact`,
};
} else {
return { text: "✗ Audit chain verification failed: integrity compromised" };
}
} catch (error) {
return { text: `Error verifying chain: ${error}` };
}
}
/**
* Export audit log.
*/
async function exportAuditLog() {
try {
const { sessionDir, auditDir } = getWeb4Paths();
const sessions = await readdir(sessionDir);
if (sessions.length === 0) {
return { text: "No audit logs to export." };
}
// Get latest session
const latestSessionFile = sessions.sort().reverse()[0];
const sessionId = latestSessionFile.replace(".json", "");
const auditFile = join(auditDir, `${sessionId}.jsonl`);
const text = [
`Audit log exported: ${auditFile}`,
"",
"Copy this file to share with auditors or relying parties.",
"The JSONL format preserves the hash-linked chain structure.",
].join("\n");
return { text };
} catch (error) {
return { text: `Error exporting audit log: ${error}` };
}
}

View File

@ -0,0 +1,197 @@
/**
* Web4 Governance Extension - R6 Workflow
* SPDX-License-Identifier: MIT
* Copyright (c) 2025 Web4 Contributors
*
* R6 Framework: Rules + Role + Request + Reference + Resource Result
*/
import { createHash, randomBytes } from "node:crypto";
import { appendFile, mkdir } from "node:fs/promises";
import { join } from "node:path";
import type { R6Request, AuditRecord, SessionState } from "./types.js";
import { getWeb4Paths } from "./session.js";
/**
* Create an R6 request for a tool call.
*/
export function createR6Request(
session: SessionState,
toolName: string,
params: Record<string, unknown>,
): R6Request {
const requestId = `r6:${randomBytes(4).toString("hex")}`;
const actionIndex = session.action_count;
const prevR6 = session.r6_requests[session.r6_requests.length - 1];
// Categorize tool
const category = categorizeToolCall(toolName, params);
const target = extractTarget(toolName, params);
const r6: R6Request = {
request_id: requestId,
rules: session.preferences,
role: {
session_id: session.session_id,
action_index: actionIndex,
},
request: {
tool: toolName,
category,
target,
},
reference: {
chain_position: actionIndex,
prev_r6_id: prevR6?.request_id,
},
timestamp: new Date().toISOString(),
};
return r6;
}
/**
* Finalize R6 request with result.
*/
export function finalizeR6Request(
r6: R6Request,
result: unknown,
error?: string,
): R6Request {
const status = error ? "error" : "success";
const outputHash = result
? createHash("sha256")
.update(JSON.stringify(result))
.digest("hex")
.slice(0, 16)
: undefined;
return {
...r6,
result: {
status,
output_hash: outputHash,
error,
},
};
}
/**
* Create an audit record from R6 request.
*/
export function createAuditRecord(
r6: R6Request,
session: SessionState,
): AuditRecord {
const recordId = `audit:${randomBytes(4).toString("hex")}`;
const prevRecord = session.audit_chain[session.audit_chain.length - 1];
const prevRecordHash = prevRecord
? createHash("sha256")
.update(JSON.stringify(prevRecord))
.digest("hex")
.slice(0, 16)
: undefined;
return {
record_id: recordId,
r6_request_id: r6.request_id,
tool: r6.request.tool,
category: r6.request.category,
target: r6.request.target,
result: r6.result!,
provenance: {
session_id: session.session_id,
action_index: r6.role.action_index,
prev_record_hash: prevRecordHash,
},
timestamp: r6.timestamp,
};
}
/**
* Persist R6 request to daily log.
*/
export async function persistR6Request(r6: R6Request): Promise<void> {
const { r6Dir } = getWeb4Paths();
await mkdir(r6Dir, { recursive: true });
const date = new Date().toISOString().split("T")[0];
const logFile = join(r6Dir, `${date}.jsonl`);
await appendFile(logFile, JSON.stringify(r6) + "\n");
}
/**
* Persist audit record to session log.
*/
export async function persistAuditRecord(
record: AuditRecord,
sessionId: string,
): Promise<void> {
const { auditDir } = getWeb4Paths();
await mkdir(auditDir, { recursive: true });
const logFile = join(auditDir, `${sessionId}.jsonl`);
await appendFile(logFile, JSON.stringify(record) + "\n");
}
/**
* Categorize tool call for audit trail.
*/
function categorizeToolCall(
toolName: string,
params: Record<string, unknown>,
): string {
// Basic categorization - can be extended
if (toolName.toLowerCase().includes("read") || toolName.toLowerCase().includes("get")) {
return "read";
}
if (
toolName.toLowerCase().includes("write") ||
toolName.toLowerCase().includes("create") ||
toolName.toLowerCase().includes("edit")
) {
return "write";
}
if (
toolName.toLowerCase().includes("delete") ||
toolName.toLowerCase().includes("remove")
) {
return "delete";
}
if (
toolName.toLowerCase().includes("exec") ||
toolName.toLowerCase().includes("run") ||
toolName.toLowerCase().includes("bash")
) {
return "execute";
}
return "other";
}
/**
* Extract target from tool parameters.
*/
function extractTarget(
toolName: string,
params: Record<string, unknown>,
): string | undefined {
// Common parameter names for targets
const targetKeys = [
"file_path",
"path",
"filename",
"url",
"target",
"destination",
"command",
];
for (const key of targetKeys) {
if (params[key]) {
return String(params[key]);
}
}
return undefined;
}

View File

@ -0,0 +1,115 @@
/**
* Web4 Governance Extension - Session Management
* SPDX-License-Identifier: MIT
* Copyright (c) 2025 Web4 Contributors
*/
import { createHash, randomBytes } from "node:crypto";
import { readFile, writeFile, mkdir } from "node:fs/promises";
import { homedir, userInfo } from "node:os";
import { join } from "node:path";
import type {
SessionToken,
GovernancePreferences,
SessionState,
} from "./types.js";
const WEB4_DIR = join(homedir(), ".web4");
const SESSION_DIR = join(WEB4_DIR, "sessions");
const PREFERENCES_FILE = join(WEB4_DIR, "preferences.json");
/**
* Create a software-bound session token.
* In the full Web4 spec, this would be an LCT (Linked Context Token)
* bound to hardware. This light version uses a software-derived token.
*/
export function createSessionToken(): SessionToken {
const uid = userInfo().uid;
const hostname = require("node:os").hostname();
const timestamp = new Date().toISOString();
const seed = `${hostname}:${uid}:${timestamp}:${randomBytes(8).toString("hex")}`;
const tokenHash = createHash("sha256").update(seed).digest("hex").slice(0, 12);
return {
token_id: `web4:session:${tokenHash}`,
binding: "software",
created_at: new Date().toISOString(),
machine_hint: createHash("sha256").update(hostname).digest("hex").slice(0, 8),
};
}
/**
* Load user governance preferences.
*/
export async function loadPreferences(): Promise<GovernancePreferences> {
try {
const data = await readFile(PREFERENCES_FILE, "utf-8");
return JSON.parse(data);
} catch {
// Default preferences
return {
auditLevel: "standard",
showR6Status: true,
actionBudget: null,
};
}
}
/**
* Initialize Web4 session state.
*/
export async function initializeSession(sessionId: string): Promise<SessionState> {
await mkdir(SESSION_DIR, { recursive: true });
const token = createSessionToken();
const preferences = await loadPreferences();
const session: SessionState = {
session_id: sessionId,
token,
preferences,
started_at: new Date().toISOString(),
action_count: 0,
r6_requests: [],
audit_chain: [],
};
const sessionFile = join(SESSION_DIR, `${sessionId}.json`);
await writeFile(sessionFile, JSON.stringify(session, null, 2));
return session;
}
/**
* Load session state.
*/
export async function loadSession(sessionId: string): Promise<SessionState | null> {
try {
const sessionFile = join(SESSION_DIR, `${sessionId}.json`);
const data = await readFile(sessionFile, "utf-8");
return JSON.parse(data);
} catch {
return null;
}
}
/**
* Save session state.
*/
export async function saveSession(session: SessionState): Promise<void> {
const sessionFile = join(SESSION_DIR, `${session.session_id}.json`);
await writeFile(sessionFile, JSON.stringify(session, null, 2));
}
/**
* Get Web4 directory paths.
*/
export function getWeb4Paths() {
return {
web4Dir: WEB4_DIR,
sessionDir: SESSION_DIR,
preferencesFile: PREFERENCES_FILE,
auditDir: join(WEB4_DIR, "audit"),
r6Dir: join(WEB4_DIR, "r6"),
};
}

View File

@ -0,0 +1,74 @@
/**
* Web4 Governance Extension - Types
* SPDX-License-Identifier: MIT
* Copyright (c) 2025 Web4 Contributors
*/
export type SessionToken = {
token_id: string;
binding: "software";
created_at: string;
machine_hint: string;
};
export type GovernancePreferences = {
auditLevel: "minimal" | "standard" | "verbose";
showR6Status: boolean;
actionBudget: number | null;
};
export type R6Request = {
request_id: string;
rules: Record<string, unknown>;
role: {
session_id: string;
action_index: number;
};
request: {
tool: string;
category: string;
target?: string;
};
reference: {
chain_position: number;
prev_r6_id?: string;
};
resource?: {
estimated_cost?: number;
};
result?: {
status: "success" | "error" | "blocked";
output_hash?: string;
error?: string;
};
timestamp: string;
};
export type AuditRecord = {
record_id: string;
r6_request_id: string;
tool: string;
category: string;
target?: string;
result: {
status: "success" | "error" | "blocked";
output_hash?: string;
error?: string;
};
provenance: {
session_id: string;
action_index: number;
prev_record_hash?: string;
};
timestamp: string;
};
export type SessionState = {
session_id: string;
token: SessionToken;
preferences: GovernancePreferences;
started_at: string;
action_count: number;
r6_requests: R6Request[];
audit_chain: AuditRecord[];
};