lamps

lamps — the docs

A lamp is a folder with a ceiling: a manifest (lamp.json) that declares what the program may touch, and a program (lamp.syn) that does one job. You run it from the terminal; your agent gets it as a tool; the runtime enforces the ceiling. This page is everything, in order: install, pull, run, update, create, publish, the rules.

1 · Install the CLI

curl -fsSL https://lamps.sh/install | sh        # macOS, Linux
irm https://lamps.sh/install.ps1 | iex          # Windows

The script detects your OS, downloads the lamp binary from the GitHub release, verifies its sha256, puts it in ~/.lamps/bin and adds that to your PATH. It is forty lines; read it first. lamp upgrade checks for a newer release and tells you how to get it.

2 · Pull (download a lamp)

lamp pull git                       official set (github.com/synsema/lamps)
lamp pull owner/repo                one lamp per repo: lamp.json at the repo root
lamp pull owner/repo@v1.2.0         a tag; without @, the latest tag (main if none)
lamp pull owner/repo/name           MANY lamps per repo: a folder per lamp
lamp pull github.com/owner/repo     full URL, same thing
lamp pull ./my-lamp                 a local folder
lamp add <ref>                      alias of pull

pull fetches the files the manifest lists, one by one, straight from the git host — no git, no archive, no install step, nothing executed. Each file gets a sha256 recorded in .pulled next to the lamp. Then the promise linter runs: if the source asks for anything the manifest never declared, you see BREAKS ITS PROMISE with each line, before anything can run.

lamp list
lamp inspect <lamp>

3 · Run

lamp run git log '{"n": 5}'
lamp git log '{"n": 5}'
LAMP_TIMEOUT=60 lamp run ...

Every run is a child process under the effective ceiling — manifest ∩ project ∩ session. The lamp prints one JSON value. A refusal by the lamp's own policy comes back as data; a refusal by the runtime comes back as structured DENIED entries with the reason. That is the system working — never retry with a wider ceiling. Every check, granted or denied, lands in lamp audit.

4 · Update

lamp update <ref>

update is pull of the latest tag. Hashes decide: if no file changed, nothing is written and nothing is counted twice on the hub — a machine counts once per lamp per day, whatever it does.

5 · Create a lamp

A lamp is one folder with two files. Make it, check it, run it locally — no registration, no account, no tooling beyond the CLI and the Synsema runtime.

my-lamp/
├─ lamp.json     the manifest
├─ lamp.syn      the program (Synsema)
└─ README.md
{
  "name": "my-lamp",
  "version": "0.1.0",
  "description": "One line: what it does and what it refuses.",
  "profile": "pure",
  "caps": "stdout,env=LAMP_*,net=api.example.com",
  "files": ["lamp.syn"],
  "tools": [
    { "name": "fetch",
      "description": "What an agent reads to decide.",
      "parameters": { "type": "object", "properties": { "id": { "type": "integer" } } } }
  ]
}

The program reads four environment variables and prints one JSON value:

LAMP_TOOL    "fetch"
LAMP_ARGS    {"id": 5}
LAMP_ROOT    the project directory
LAMP_DIR     the lamp's own folder
intent: "one line, frozen at startup"
require env("LAMP_*")
require net("api.example.com")

task main()
    let a be json_decode(env("LAMP_ARGS", "{}"))
    let r be http_get("https://api.example.com/item/" + text(floor(number(a["id"]))))
    print(json_encode({"ok": r["ok"], "status": r["status"]}))

when env("LAMP_TOOL", "") != ""
    main()

test "ids are numbers"
    assert_eq(floor(number("5")), 5)

Check it before anyone pulls it:

synsema test lamp.syn
lamp inspect ./my-lamp
lamp run ./my-lamp fetch '{"id": 5}'

6 · Publish (appear on the hub)

Publishing is pushing to a public repo. There is no account and no login — your git host identity is your identity. Identity is the address, GitHub-style: nobody can publish under vercel/… without controlling github.com/vercel, and the manifest's name must equal the repo name (or the folder name in a multi-lamp repo) — a mismatch is refused at pull and never listed. The hub lists a lamp automatically the first time anyone pulls it: it fetches the manifest and the program, validates them, runs the promise linter, and puts it in the catalog with honest labels — community, and BREAKS ITS PROMISE when the source asks above its declared ceiling.

# one lamp per repo: lamp.json at the root, name == repo
git push
lamp publish owner/repo

# many lamps per repo: a folder per lamp, name == folder
lamp pull owner/repo/name

# the badge for your README
![lamp pulls](https://lamps.sh/badge/owner/name.svg)

Identity on the hub is owner/name, first-come per name and always shown with its owner — the owner in front of every name is the trust signal. Versions are git tags; without tags the default branch is used. The official namespace (lamp pull git) is the curated monorepo; everything else is the open ecosystem.

7 · The rules

8 · For agents (MCP + skill)

lamp mcp

Every enabled lamp's tools appear as lamp_<name>_<tool> with the effective ceiling stated in each description, so the model knows its own limits. lamps_list and lamps_audit are always there; lamps_eval exists only when a human wrote ~/.lamps/session.json with "eval": true. It starts from the working directory, so the tools change with the project and the agent's configuration does not. A lamp that is installed but not enabled is invisible to the model.

lamp skill      # writes ./.agents/skills: the lamps skill, plus one SKILL.md per lamp you pulled

9 · Where things live, and the ceilings you set

<project>/.lamps/<name>/              committing IS enabling
<project>/.lamps/<name>/policy.json   config-only for a global lamp
<project>/.lamps/config.json          the project ceiling
~/.lamps/lamps/<owner>/<name>/        pulled lamps
~/.lamps/enabled.json                 which globals agents may see
~/.lamps/session.json                 the ceiling over everything
~/.lamps/audit/log.jsonl              asked · granted · denied

Precedence: project over user, like every tool with a project config. lamp init writes a starting ./.lamps/config.json; tighten it and commit it — a tighter ceiling costs fewer prompts, not more.

10 · Trust, honestly

The paper — LAMP: Least-Authority Module Protocol The official lamps The CLI source