---
title: Install and log in
description: Install the stamp CLI, log in as yourself, and clone through Stamp.
llms_section: "For agents"
llms_order: 110
llms_summary: "Read first: install the CLI, log in, the commit email, SSH and host keys, and the full command list."
---

# Install and log in

Everything an agent sets up once, before its first push.

::: tip Your Stamp host
These pages use the default host, `stamp.build`, and its CLI, `stamp`. For a self-hosted Stamp, point the CLI at it with the environment variables `STAMP_URL` (the instance address) and `STAMP_SERVICE` (support in a config file is coming soon); that host's own `/llms.txt` names its address, its service and its SSH host.
:::

## Which binary (read this first)

Use `stamp` for this service. It pins serviceDefault=stamp; there is
nothing to configure.

Install the latest CLI. Version 0.3.3 or later is the minimum that pairs the
service slug and URL safely; this guide assumes 0.3.11 or later, and the CLI
tells you once a day when a newer one exists.

## Quick start

```sh
# 1. Raft-side session (no human OAuth, no browser)
raft integration login --service "stamp"

# 2. Install the client (public, no credentials)
curl -fsSL https://stamp.build/install.sh | sh         # linux/macos -> ~/.local/bin
# Windows PowerShell:  irm https://stamp.build/install.ps1 | iex
# Or with npm (node >=20):  npm i -g @botiverse/stamp-cli@latest
# Shared machine user: install into a directory of your own and put it
# first on PATH (a shared default may already hold another agent's copy):
#   curl -fsSL https://stamp.build/install.sh | STAMP_DEST=DIR sh
#   export PATH="DIR:$PATH"
# Or with npm (check `npm prefix -g` first; DIR/bin goes on PATH):
#   npm_config_prefix=DIR npm i -g @botiverse/stamp-cli@latest
#   export PATH="DIR/bin:$PATH"
# The install scripts honor STAMP_VERSION and STAMP_DEST.

# 3. Log in and confirm you are yourself
stamp login
stamp whoami
# principalType=agent and your own name; it also prints your commit email.

# 4. Clone through Stamp, then commit and push as usual
stamp repo clone OWNER/REPO
```

That is all the setup. Details:

- **Commit email.** Every agent has one without doing anything:
  `<server>+<handle>@agents.stamp.build`, which receives no mail. `repo clone`
  (and `repo use`) write it, with the agent's name, into that checkout's own
  git config, so commits there are yours. Only if you have an address of your
  own: `stamp identity bind --email ADDR` (never a human's).
- **Who must have made a commit.** Every commit a push adds to the repository
  must be *committed* by your commit email (`git log --format=%ce`); its
  *author* may be anyone. A cherry-pick, a backport or a rebased upstream
  commit keeps its original author and is accepted as long as you committed
  it from your checkout. Commits already on the repository's branches are not
  checked. A commit someone else committed is refused as `IDENTITY_MISMATCH`.
- **SSH key.** There is nothing to do: `stamp login` (or the first
  `repo clone`) creates this agent's own key in its own directory, beside its
  Stamp session, enrolls it and says so in one line. Nothing goes into
  `~/.ssh`. Several agents often share one machine user: `~/.ssh`,
  `~/.ssh/config` and global git settings are shared by all of them, and a push
  made with another agent's key is that agent's push. Only to use a key of your
  own choosing: `stamp key enroll --public-key <key>.pub`.
- **Checkouts.** `repo clone` writes the key and the pinned host key into that
  checkout's own `core.sshCommand`, so plain `git push`/`git fetch` work there.
  For a checkout you did not clone through Stamp, run `stamp repo use [DIR]`
  once; it changes that repository only, never a global setting.
- **Shared clones.** When several agents work in one clone, each in its own
  worktree (`git worktree add`), the clone's config is shared by all of them.
  There `repo use` writes one line to the shared config,
  `extensions.worktreeConfig=true`, and writes the key, the commit identity
  and each GitHub remote's push url (`remote.NAME.pushurl` at the gateway)
  into that worktree's own config. It never changes a shared url. What else
  it writes depends on the shape of the shared origin. A GitHub https fetch
  url keeps working, so fetches stay on https and only pushes go through
  Stamp, by that push url. A GitHub ssh fetch url (`git@github.com:o/r.git`,
  `ssh://git@github.com/o/r`) would fail here, since this worktree's
  `core.sshCommand` pins Stamp's key and host key; so for a repository Stamp
  covers, repo use writes `url.<gateway url>.insteadOf` rules for exactly
  that repository's ssh spellings (with and without `.git`), in this
  worktree only, and fetches go through the gateway with this agent's key,
  as after `repo clone`. Not GitHub https: for a private repository that
  would need an https credential on the machine, which is missing or
  someone else's (a human's `gh` login), and Stamp never borrows one. A
  shared push url to GitHub (https or ssh) of a covered repository gets
  `url.<gateway url>.insteadOf <that url>` in this worktree instead of a
  push url of its own, so pushes go to the gateway only. Git applies such a
  rule to every url that starts with it, so none is written when it would
  also rewrite another repository's remote, and none for a repository Stamp
  does not cover: repo use then warns instead. It prints each rule it
  writes, and for each remote whether it wrote a push url. A worktree-level
  `remote.NAME.url` does not help: git fetches from the first url, the
  shared one, and pushes to both when there is no push url; repo use
  removes one that is the gateway url and names any other. It names, and
  leaves alone, any identity, key or push url the shared config still sets:
  a shared push url is pushed to as well. A remote whose shared push url is
  already the gateway gets no push url of its own.

## Host key

`stamp repo clone` and `repo use` pin the gateway's SSH host key for you, from
`https://stamp.build/.well-known/ssh-known-hosts`, in this agent's own known_hosts file. Do
not accept a host key blindly (`StrictHostKeyChecking=no`, or "yes" at a prompt).

That endpoint is this instance reporting on itself. To check it from outside,
compare it with what the SSH port presents:

```sh
curl -s https://stamp.build/.well-known/ssh-known-hosts | ssh-keygen -lf -
ssh-keyscan -t ed25519 ssh.stamp.build | ssh-keygen -lf -
```

ssh-keyscan is the one that observes the key on the wire, from outside the instance.
The two must match.

## Commands

```sh
stamp login | whoami [--json]
stamp key enroll [--public-key FILE] | key list | key revoke KEY_ID
stamp identity bind --email ADDR
stamp repo clone OWNER/REPO [DIR] | repo use [DIR]
git push                                  (from a Stamp checkout)
stamp receipt get ID | receipt list [--repo OWNER/REPO]
stamp pr create --receipt ID --base BRANCH --title TITLE --body-file FILE
stamp pr list | pr view | pr review | pr diff | pr checks  NUMBER --repo OWNER/REPO
stamp pr view NUMBER --repo OWNER/REPO --comments [--cursor CURSOR]   (reviews and comments, oldest first; external authors' text is marked untrusted)
stamp pr approve | pr unapprove  NUMBER --repo OWNER/REPO --head SHA --base SHA
stamp pr review-request NUMBER --repo OWNER/REPO --reviewer @HANDLE [--reviewer @HANDLE]... [--remove]   (ask an agent or person of your server to review; an agent gets an event with the approve command, a person is asked on GitHub too)
stamp pr list --review-requested   (the pull requests you are asked to review)
stamp search QUERY... [--limit N] [--cursor CURSOR]   (pull requests by title, body, discussion, head SHA or branch; a number puts that PR first: `1234`, `#1234`, `OWNER/REPO#1234` (that repository only) or its GitHub or Stamp URL, alone or among words)
stamp pr merge NUMBER --repo OWNER/REPO --head SHA --base SHA [--auto] [--method squash]
stamp pr comment | pr edit | pr close  NUMBER --repo OWNER/REPO
stamp pr close NUMBER --repo OWNER/REPO --reason TEXT   (server admin: close a stale PR from your server)
stamp issue list --repo OWNER/REPO [--state open|closed|all] [--label NAME]... [--limit N]
stamp issue view NUMBER --repo OWNER/REPO [--comments]
stamp issue create --repo OWNER/REPO --title TITLE [--body-file FILE] [--label NAME]...
stamp issue comment | issue edit | issue close | issue reopen  NUMBER --repo OWNER/REPO   (written as the Stamp App, signed with your name)
stamp run list --repo OWNER/REPO [--branch B] [--workflow NAME|ID|FILE] [--status S] [--limit N]
stamp run view RUN_ID --repo OWNER/REPO [--job JOB_ID] [--log | --log-failed]   (the end of each job's log)
stamp run rerun RUN_ID --repo OWNER/REPO [--failed | --job JOB_ID]   (agent: runs started by a pull request only (event pull_request/pull_request_target with the pull request still open, or merge_group; else RUN_RERUN_NOT_ALLOWED: a person reruns it on GitHub); latest run of its workflow+branch only; a successful run needs a person; outcome unknown: run view first, never resend)
stamp run download RUN_ID --repo OWNER/REPO [--name NAME]... [--pattern GLOB]... [--dir DIR]   (artifacts of any size, straight from GitHub's storage, sha256-checked; one --name: into DIR, else DIR/NAME/; or --artifact-id ID)
stamp workflow list --repo OWNER/REPO [--all]   (the repository's workflows: name, state, id, file, and whether agents may start it: whether the repository's workflow write is granted)
stamp workflow run FILE|ID|NAME --repo OWNER/REPO [--ref BRANCH_OR_TAG] [-f NAME=VALUE]...   (start a workflow_dispatch run; ref defaults to the default branch; agent: runs only once your server has granted its agents workflow write of the repository — one grant per repository, aligned with GitHub's own permission model, not a per-workflow list — and then ANY workflow of it, on ANY branch or tag GitHub accepts, exactly as a GitHub token with Actions write may; without the grant WORKFLOW_DISPATCH_NOT_ALLOWED and Stamp asks your server's owners and admins for you (one request per repository, already pending or filed now; the refusal prints its link, you are told when one of them decides, and nothing runs until you run it again); as the Stamp App, and run list/view name you; prints the run id when GitHub returns it, else run list --workflow ID finds it; outcome unknown: run list first, never resend. A workflow carrying secrets is protected the way GitHub itself protects it, with a GitHub Environment (deployment branch rules, required reviewers), not by Stamp)
stamp workflow allowed [--repo OWNER/REPO]   (the repositories your server's agents are granted workflow write of)
stamp request workflow --repo OWNER/REPO [--note TEXT]   (agent: ask your server's owners and admins ahead of a run, for the repository's workflow write as a whole; any one of them approves it on the link it prints; workflow run files the same request on its own when refused)
stamp workflow allow | workflow disallow  OWNER/REPO   (a person who owns or administers the server only; grants or revokes the repository's workflow write directly; an agent asks with request workflow, or by running it)
stamp request admin-api rfs_ID --reason TEXT [--to @HUMAN]   (agent: when `stamp api` refuses a ruleset, branch protection, environment or repository settings call, the refusal keeps it as rfs_ID for 7 days; this asks a person with admin rights on the repository (your server's owners and admins, or @HUMAN) to run exactly that call once, as themselves; you are told GitHub's answer. Nothing is filed until you run it, and --reason is required)
stamp request admin-api --method PUT --path repos/OWNER/REPO/rulesets/ID [--body @FILE] --reason TEXT   (the same, for a call you give directly; only those four kinds of call are accepted: deleting or transferring a repository, access, keys, hooks and secrets stay a person's to do on GitHub)
stamp app request [--repo OWNER/REPO]   (ask your server's owners and admins for its GitHub App, or to cover a repository)
stamp api ENDPOINT [-X METHOD] [-f K=V] [-F K=V|K=@FILE] [--input FILE|-] [--paginate]   (any other GitHub call, like gh api)
```
