Interfaces
One operation. Every surface. The same policy.
Adapters are outer layers. A capability granted to a browser tool is the capability the CLI and an MCP client invoke. None of them can infer authority from the presence of a route, a tool description, or a document.
01 — Capability model
A capability is a contract, not an endpoint.
Every operation declares an ID, input and output schemas, a risk class, a required scope, expected side effects, and possible errors. The registry, not the route table, is the source of truth for what an actor may do.
| Risk | Examples | Default |
|---|---|---|
| read | workspace.list, object.get, search.query, graph.neighbors | Allowed within scope |
| derive | context.bundle, snapshot.verify, index.status | Allowed; bounded resources |
| propose | change.preview, task.start, homepage.preview | Creates a reviewable proposal |
| mutate | change.apply, homepage.publish, backup.create | Explicit approval + version precondition |
| admin | token.issue, policy.grant, workspace.remove | Separate elevation; never delegated to a model |
02 — REST
/api/v1, logical IDs, typed errors.
Resource paths use object and workspace IDs, never absolute paths. Writes carry idempotency keys and revision preconditions. Errors are problem details, not stack traces.
| Method & path | Purpose |
|---|---|
| GET /health | Daemon, catalog, watcher, extract state |
| GET /api/v1/workspaces | List registered workspaces |
| POST /api/v1/workspaces | Register a directory |
| GET /api/v1/objects/{id} | Object metadata and provenance |
| GET /api/v1/objects/{id}/content | Authorized content range |
| GET /api/v1/search | Permission-filtered full-text search |
| POST /api/v1/context/bundles | Budgeted context assembly |
| POST /api/v1/graph/query | Bounded typed traversal |
| POST /api/v1/changes/preview | Three-way diff, no write |
| POST /api/v1/changes/apply | Approved, versioned write |
| POST /api/v1/tasks, GET /{id} | Durable async work |
| GET /api/v1/audit | Ledger entries and receipts |
GET /api/v1/search?query=architecture &workspace_id=wks_01JEX Accept: application/json 200 OK { "results": [ { "object_id": "obj_01J9F2", "version": "sha256:8b708e…", "title": "workspace-architecture.md", "score": 0.82, "freshness": "indexed 12s ago", "provenance": "observed" } ], "next_cursor": null }
code, message, request_id, optional field_errors, retry_after, conflict_version, approval_request_id. A 403 never reveals the existence of a hidden object.03 — MCP
Narrow tools for agents that must ask.
Tool annotations are hints; the server enforces. Discovery shapes are separated from execution, and a destructive tool normally returns a preview or an approval request rather than acting.
| Tool | Risk |
|---|---|
intralock_workspace_list | read |
intralock_workspace_inspect | read |
intralock_object_get | read |
intralock_object_read | read |
intralock_search | read |
intralock_graph_query | read |
intralock_context_bundle | derive |
intralock_change_preview | propose |
intralock_change_apply | mutate · approval |
intralock_task_start | derive · durable |
intralock_snapshot_verify | read |
intralock_capability_list | read |
Resources use intralock://workspace/{id}/…. A URI is a locator, not a bearer credential.
{
"name": "intralock_search",
"arguments": {
"workspace_id": "wks_01JEX",
"query": "vendor renewal",
"limit": 5
}
}
→ typed results, each with
object_id, version, freshness,
provenance, source_url
04 — CLI
Useful on its own, scriptable by design.
Human output on stderr, machine output on stdout. JSON is schema-versioned and free of ANSI sequences. Exit codes distinguish input error, policy denial, conflict, unavailable daemon, partial completion, and internal fault.
# attach and inspect intralock init --workspace . --read-only intralock workspaces list intralock workspace inspect wks_01JEX # index and search intralock index run --workspace wks_01JEX --dry-run intralock index status --workspace wks_01JEX intralock search "architecture" --workspace wks_01JEX --json # relationships and context intralock graph neighbors obj_01J9F2 --depth 2 intralock context bundle --workspace wks_01JEX \ --mode engineering --max-tokens 6000 # governance and evidence intralock policies explain --actor agt_7f \ --action read_content --object obj_01J9F2 intralock snapshot create --workspace wks_01JEX intralock snapshot verify --workspace wks_01JEX intralock doctor --full
Global flags
--workspace,--profile,--format--json,--quiet,--verbose--timeout,--request-id--dry-run,--expected-version--yesonly where safe — never to bypass approval
Exit codes
0success ·2input error3policy denial ·4conflict5daemon unavailable ·6partial completion1internal fault
05 — GraphQL
Exploration with a budget.
Read-only query types for workspaces, objects, versions, edges, evidence, tasks, capabilities, and snapshots. Mutations route through the same preview and approval services as REST — never direct writes to graph edges.
query WorkspaceOverview($id: ID!) {
workspace(id: $id) {
name
freshness
objects(first: 10, type: DOCUMENT) {
edges { node { id title version } }
}
capabilities { id risk }
}
}
Depth, field cost, result cardinality, and time budgets are enforced. Introspection is limited outside authorized development contexts. A partially unauthorized relationship is omitted, with bounded redaction metadata — never a hidden name, count, or snippet in an error.
Data loaders batch reads without losing per-object checks. Subscriptions authorize at connection and again at delivery.
06 — WebMCP & async
Browser tools, same server checks.
A browser page registers only context-relevant capabilities. Every call hits the same authenticated HTTP endpoint as the human action, with origin checks and session policy. No tokens or filesystem secrets in page JavaScript.
// Illustrative: follow the tested draft's signature. if (document.modelContext?.registerTool) { document.modelContext.registerTool({ name: "search_current_workspace", description: "Search the open workspace only.", inputSchema: { type: "object", properties: { query: { type: "string" } } }, execute: ({ query }) => fetch("/api/v1/search", { method: "POST", … }) }); }
Long-running work
Index refresh, large export, graph rebuild, and approved work orders return a durable task ID. Task metadata binds actor, scope, idempotency key, expiry, progress, and outcome digest. Polling is rate-limited; cancellation is re-authorized.
Events travel over bounded SSE or webhooks, authorized at subscribe time and again at delivery. A task ID is never sufficient authorization on its own.