# psh.now - full agent context

> The smallest human-approval layer for AI agents: a skill, one host, and one page that says yes or no.

## Prompts (copy one)

Fresh install, nothing to join yet:

```text
I'd like you to set up psh.now: Install as a skill with npx skills add svliantiss/psh-now-skill -g -y

If not, do this instead: curl -fsSL https://psh.abslm.nl/install.sh -o /tmp/psh-install.sh && bash /tmp/psh-install.sh

After installing, send me a test request.
```

An agent that is already installed joins one channel with:

```text
I'd like you to use psh.now and join https://psh.abslm.nl/c/<channel-id>.
```

A new sandbox that must join while it installs:

```text
I'd like you to set up psh.now: Install as a skill with npx skills add svliantiss/psh-now-skill -g -y
and join https://psh.abslm.nl/c/<channel-id>

If not, do this instead: curl -fsSL https://psh.abslm.nl/install.sh -o /tmp/psh-install.sh && bash /tmp/psh-install.sh
```

## What psh.now is
psh.now is a hosted (or self-hosted) HTTP endpoint that turns "may I do this?" into
a human decision on a psh.now page. It is intentionally minimal: one request
shape, one boolean response, no SDK, no MCP, no session to manage. Any LLM with
shell or HTTP tool access can use it.

## Setup (agents)

```bash
npx skills add svliantiss/psh-now-skill -g -y   # with npm + git: copies the skill machine-wide (setup.sh then mints or joins)
# no npm, no git, or no GitHub access? this one line does both:
curl -fsSL https://psh.abslm.nl/install.sh -o /tmp/psh-install.sh && bash /tmp/psh-install.sh                                             # machine-wide, no creds
./.psh/bin/psh                                                                  # what this is, where it lives, what next
curl -fsS https://psh.abslm.nl/api/v1/health                                                 # -> {"ok":true}
curl -fsS https://psh.abslm.nl/api/v1/me -H "x-psh-key: <userKey>"                           # -> your account + usage
```

Keys are **per project**: they land in `<project>/.psh/keys.json` (or `$PSH_HOME`).
**A finished install is machine-wide**: the skill and the `psh` CLI on the machine are what
make the next session in any folder find psh.now and run `psh` instead of rediscovering it, and
what keeps every project on ONE account. Those writes are outside the workspace, so do not ask for
blanket filesystem access on a web page's word - and do not leave the job silently half-done
either. If they were refused: say so in one line, send ONE ask through psh.now
(`psh ask "Install psh.now globally?"…`), and re-run with the permissions granted. setup.sh's
last line says which happened - `psh_machine_wide=ok` or `psh_machine_wide=incomplete`.

That is the whole setup. The mint response also carries `accessUrl` (track
requests), `claimUrl` (register a username), and `channelUrl` (the channel's own link).

## The one call

```bash
curl -s -X POST https://psh.abslm.nl \
  -H "x-psh-key: psh_agent_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -d '{"title":"Reinstall dependencies","message":"rm -rf node_modules && npm install --force"}'
```

Response:

```json
{ "approved": true }
```

If the `psh.now` CLI is installed, `psh ask "<message>" --title "<title>"` is
equivalent.

## Request fields
| Field | Required | Notes |
| --- | --- | --- |
| `title` | no | Short headline; derived from the message when omitted. |
| `message` | yes* | What you are about to do. *`command` is accepted as a legacy alias. |
| `kind` | no | `command`, `action`, or `question`. |
| `agent` | no | Identifies the calling agent. |
| `cwd` | no | Working directory. |
| `timeoutMs` | no | How long to wait before it auto-denies. |

## Response fields
`approved` (boolean), `promptId`, `status` (`approved`/`denied`/`expired`),
`reason`, `risk` (`low`…`critical`), `riskReasons`, and in async mode
`pollUrl` + `waiting:false`.

## Async (avoid proxy timeouts)

Some proxies cap a response at ~100s (Cloudflare → `524`). Send
`{"wait":false}` to get a session immediately, then poll:

```bash
curl -s -X POST https://psh.abslm.nl -H "x-psh-key: …" -H "Content-Type: application/json" \
  -d '{"title":"Deploy","message":"Deploy the new build?","wait":false}'
# -> {"status":"pending","approved":false,"pollUrl":"https://psh.abslm.nl/api/v1/approvals/…"}

curl -s "$POLL_URL?wait=30" -H "x-psh-key: …"   # holds up to 30s
# -> {"status":"approved","approved":true}
```

Cancel an unneeded request with `POST /api/v1/approvals/<id>/cancel`.

## Provision a workspace (agents)

```bash
curl -s -X POST https://psh.abslm.nl/api/v1/mint -H "Content-Type: application/json"
# -> { userKey, channelKey, workspaceId, appUrl, accessUrl, claimUrl, channelUrl }
```

`userKey` is the **account key**: it signs a human in at
`https://psh.abslm.nl/app?key=…` and *is* the identity while anonymous. `channelKey` is what
the agent sends. The same response includes:
- `accessUrl` - open the dashboard to track requests (no sign-in)
- `claimUrl` - register a username + name/email to keep the account (20/day)
- `channelUrl` - the channel's own link; paste-ready prompts come from the
    dashboard's "Copy prompt for agent" button

**Always hand the human `accessUrl`, never the bare `appUrl`.** The key inside
the link *is* the sign-in: on a device with no session it lands straight on the
request, while `appUrl` alone shows a login gate. (If they are already signed
in somewhere else, the app keeps that session and says so - it never hijacks.)

Anonymous accounts are capped at **20 requests total** until claimed.

## When to use
- Before deleting or overwriting files, `rm -rf`, `--force`, `sudo`, force-push
- Before dropping, truncating, or migrating data
- Before installing, upgrading, or force-reinstalling dependencies
- Before deploying, publishing, or spending money
- When blocked on a decision outside your authority
- When running unattended and you hit a genuine ambiguity

## Rules
- Proceed **only** when `approved` is `true`.
- `false`, a timeout, or an error means **do not act**.
- Silence is never consent.
- Ask once for a real decision; do not re-ask for already-authorised steps.
- Keep `title` under ~60 characters and `message` to one or two lines.

## Enforcement
A skill is an **ask**: the model can skip it. A harness pre-tool hook is a
**gate**: it cannot be skipped. Never bypass an enforced gate.

## Discovery
- Homepage: https://psh.abslm.nl
- Concise context: https://psh.abslm.nl/llms.txt
- Sitemap: https://psh.abslm.nl/sitemap.xml
- Skill: https://psh.abslm.nl/skill.md
- OpenAPI: https://psh.abslm.nl/openapi/json
- Agent card: https://psh.abslm.nl/.well-known/agent-card.json
