A lamp is a distributable unit of capability: one thing you run from the terminal and one tool your agent gets, with a permission ceiling declared in its manifest and enforced by the runtime — not by trust.
This repository holds the official lamps. A folder is a lamp when it has a lamp.json
(the manifest: name, ceiling, tools, files), a lamp.syn (the program, written in
Synsema) and a SKILL.md (how an agent should use it). You can
read every line of what you are about to run; that is the point.
| lamp | what it is | ceiling |
|---|---|---|
shell |
a shell with a ceiling: runs one command only if a human-written policy allows it; destructive ones are refused before they run | exec=<your list> |
git |
read the history, never rewrite it: status, log, diff, show, blame, branches | exec=git |
npm-deps |
outdated, deprecated and vulnerable dependencies, in as few tokens as possible; never runs npm | file.read, net=registry.npmjs.org |
skills |
find, validate and read agent skills (SKILL.md) before an agent loads them | file.read |
sql |
a database the agent can read and cannot damage: SELECT only, LIMIT enforced, SQLite opened read-only | db=<your file or URL> |
http |
fetch from an allowlist of hosts and get text back, not markup | net=<twelve hosts> |
curl -fsSL https://lamps.sh/install | sh # the lamp CLI (windows: irm https://lamps.sh/install.ps1 | iex)
lamp pull git # = lamp pull synsema/git = lamp pull synsema/lamps/git
lamp git log '{"n": 5}' # runs under stdout,env=LAMP_*,exec=git — nothing else
lamp enable git # offer it to agents through `lamp mcp`
lamp skill # write the agent skills into ./.agents/skills
Every lamp here is synsema/<name> on lamps.sh; lamp pull <name>
is the shortcut. Identity is the address: a lamp is named by the GitHub owner and the
folder it lives in, never by what its JSON says.
- The grant is over effects —
net,file,db,exec,secret— not over command text. - The agent never holds the credential. It holds a hook the host implements.
- The ceiling is set once. A tighter ceiling costs fewer prompts, not more.
{
"name": "git",
"version": "0.1.0",
"description": "…",
"profile": "native",
"caps": "stdout,env=LAMP_*,exec=git",
"files": ["lamp.syn", "SKILL.md"],
"tools": [{ "name": "log", "description": "…", "parameters": { "type": "object", "properties": { … } } }]
}capsis the ceiling. The runtime enforcescaps_effective ⊆ require ∩ ceiling: nothing the program declares can rise above it, and a project or session ceiling can only narrow it.profileispure(no filesystem, no processes, no database drivers — two walls) ornative(the ceiling is the wall).filesis whatlamp pullfetches and hashes;toolsis what an agent sees over MCP, with JSON Schema parameters.{root}(the project) and{dir}(the lamp's folder) incapsand in the source are substituted by the runner.
SKILL.md lives inside the lamp's folder and is listed in files, so it is pulled,
hashed and named with the lamp — a skill can only come from the lamp it belongs to.
lamp skill writes the skills of the lamps you have into ./.agents/skills.
Copy git. Declare exactly what you need with require, read LAMP_TOOL and
LAMP_ARGS, print one JSON value, put your pure logic in test blocks
(synsema test git/lamp.syn). Ask precisely, ask small: under a ceiling the interpreter
denies, it does not trim — require net("*") under net=one.host gets nothing.
Push it to a public repo with lamp.json at the root (or a folder per lamp) and it is
owner/name on the hub the first time anyone pulls it.
Apache-2.0.