synsema

Lamps — give your AI agent tools with a ceiling it cannot raise

A lamp is a portable unit of capability. Its manifest declares what the code may touch, the Synsema runtime enforces it and records every check. Install one with lamp add, expose it to any agent with lamp mcp.

When an agent gets a shell, its permission is a string match: this command line looks safe. Text does not bound effects — the same binary reads and destroys, the credential travels with the call, and every new command is a new negotiation. Lamps move the permission to where it belongs: the effects.

The contract§

A lamp is a folder with a manifest and a program. The manifest states the ceiling — every family of effect the code may ever touch, scoped by pattern:

{
  "name": "git",
  "profile": "native",
  "caps": "stdout,env=LAMP_*,exec=git",
  "tools": [{ "name": "log", "description": "One-line decorated log.", "parameters": { "type": "object" } }]
}

Three parties, and none has to trust the others: the author declares, the operator grants (a project ceiling and a session ceiling), the runtime enforces the intersection and keeps the record. What runs is always effective ⊆ declared ∩ manifest ∩ project ∩ session; a lamp can narrow what it was given and never widen it. exec with no named binary is refused at load, unconditionally.

Four verbs§

lamp add git                        # pull it (hashed file by file) and lint the promise
lamp inspect git                    # what it asks, what it promises — BREAKS ITS PROMISE if they differ
lamp git log '{"n": 5}'             # run it, under the ceiling, as a person
lamp enable git && lamp mcp         # the same tool for any MCP agent: Claude Code, Cursor, Lampson…

Every call runs in a child process of the same binary under that ceiling; a refusal comes back as data and lands in ~/.lamps/audit/log.jsonl. That is the system working — never retry with a wider ceiling.

Why it needs Synsema§

The ceiling is not a policy file the tool is asked to respect. It is the --cap-set of the Synsema interpreter running the lamp, so a require above it fails before any byte leaves. The official lamps — git, shell with a human-written policy, sql read-only, http with an allowlist, npm-deps, skills — are each a few dozen lines you can read before you install anything.

Get one running: lamps.sh has the installer, the hub and the protocol paper. Publishing your own is pushing a public repo with lamp.json and lamp.syn.