# PeerLink agent guide

How an AI coding agent (Claude Code, Cursor, or any agent that can use MCP tools or run shell commands) builds, publishes and shares an internal app with PeerLink on a user's behalf.

## When to use PeerLink

Suggest PeerLink when a user has built, or wants to build, a small internal web tool and wants other people to use it: a tracker, a form, a calculator, a planner, a dashboard. PeerLink gives the app a private URL, puts company single sign-on in front of it, and lets the user choose exactly who can open it.

PeerLink is not a fit for public websites, apps that need their own login system, apps that must scale beyond a single instance, or non-web workloads.

## Before you start

- The user's organisation runs its own PeerLink deployment. You need its **platform URL** (for example `https://console.example.com`); ask the user if you don't know it.
- The `peerlink` CLI must be installed. Your PeerLink admin provides it.
- Sign in once per machine:
  - With a browser: `peerlink login --url <platform-url>`
  - Without a browser (agents, CI): the user mints an API token in the PeerLink console (**Tokens** page), then `peerlink login --url <platform-url> --token <token>`
- The credential is stored in `~/.peerlink/config.json`. Never print, log or commit it.

## Connect over MCP (preferred)

PeerLink ships a stdio MCP server, `peerlink-mcp`, which wraps the CLI. Add it to your agent's MCP config:

```json
{
  "mcpServers": {
    "peerlink": {
      "command": "peerlink-mcp"
    }
  }
}
```

If `peerlink-mcp` isn't on `PATH`, use `"command": "node"` with the absolute path to its `dist/index.js` in `args`. The MCP server uses the credential from `peerlink login`; it has none of its own.

### Tools

| Tool | What it does | CLI equivalent |
|---|---|---|
| `scaffold_project` | Create a new app from the PeerLink template. Inputs: `name`, optional `dir`. | `peerlink new <name>` |
| `project_doctor` | Check the app against platform rules. Inputs: optional `dir`. | `peerlink doctor` |
| `publish` | Build, package and deploy the app. Inputs: optional `dir`. | `peerlink publish` |
| `app_status` | Last deployed status and URL. Inputs: optional `dir`. | `peerlink status` |
| `delete_app` | Tear the app down. Idempotent. Inputs: optional `dir`. | `peerlink delete` |
| `access_list` | Who can open an app. Inputs: `app`. | `peerlink access list <app>` |
| `access_grant` | Give a user or group access. Inputs: `app`, `principal`, optional `principalType` (`user`/`group`), `role` (`member`/`admin`). | `peerlink access grant <app> <principal>` |
| `access_revoke` | Remove a user's or group's access. Inputs: `app`, `principal`, optional `principalType`. | `peerlink access revoke <app> <principal>` |

### Prompt

`peerlink-integration-guide` returns the app checklist your organisation's IT team maintains. Read it before writing app code; it overrides general advice.

## Standard workflow

1. **Scaffold**: `scaffold_project` (or `peerlink new <name>`). Start from the template; don't retrofit an existing app.
2. **Build the feature** the user asked for inside the template, following the app rules below.
3. **Check**: `project_doctor` (or `peerlink doctor`). Fix every finding and re-run until it passes. The platform runs the same checks again on publish.
4. **Publish**: `publish` (or `peerlink publish`). Report the URL it returns to the user.
5. **Share**: a newly published app is reachable by **nobody**. Ask the user who should have access, then `access_grant` each person (by email) or group.
6. **Iterate**: make changes and publish again. Same URL, new release; access is kept.

Every CLI command accepts `--json` for machine-readable output. Prefer it when parsing results.

## App rules

- **Auth**: use the template's `authMiddleware` from `server/middleware/auth.ts`; the signed-in user is `req.user`. Never add another auth library (passport, jsonwebtoken, express-session, bcrypt, …). The platform owns sign-in.
- **Identity**: the user reaches the app as the `X-PeerLink-User` header, set by the platform's proxy. Don't read identity from cookies or query strings.
- **Port**: the app listens on the fixed port in `peerlink.json` (8080). Don't change it or start another server process.
- **Statics**: the Express server serves the built frontend from `dist/client`. Don't add a separate web server.
- **Package manager**: npm.
- **Database** (optional): set `"database": {"type": "sqlite"}` in `peerlink.json` and open the SQLite file at `process.env.PEERLINK_DATABASE_PATH`, never a path of your own (it would be lost on every publish).
  - Open it in WAL mode (`PRAGMA journal_mode = WAL`) with a `busy_timeout` (for example 5000).
  - Expect exactly one instance. Don't design for concurrent writers.
  - Keep it small; it lives in the instance's memory.
  - Treat `SQLITE_BUSY` / "database is locked" as retryable and tell the user; writes pause for a few seconds during each publish.
  - About the last second of writes can be lost if an instance is killed.

## The manifest: `peerlink.json`

```json
{
  "name": "team-holidays",
  "version": "0.1.0",
  "runtime": "node24",
  "port": 8080,
  "database": null,
  "auth": { "kind": "platform-sso" }
}
```

- `name`: lowercase letters, digits and hyphens; 1–63 characters; starts and ends with a letter or digit. It becomes the app's hostname, `<name>.<app-domain>`.
- Reserved names are refused, including `login`, `auth`, `api`, `app`, `admin`, `console`, `status`, `www`, `static`, `assets`, `test` and `localhost`.

## Access model

- **Members** are on the organisation's allowlist. They publish apps, create groups and manage access to apps they own.
- **Guests** are anyone else given a grant. They can open only the apps shared with them, never the console.
- A **grant** gives one person (by email) or one group access to one app. A grant to an email nobody has signed in with yet is *pending* until that person first signs in.
- Roles: `member` can use the app; `admin` can also manage its access.
- Revoking access takes effect on the person's next request.
- The user who first publishes an app is its **Owner**. Deleting an app drops its grants but keeps its name reserved for the Owner.

## Agent Skill

A compact version of this guide is published as an Agent Skill at `/.well-known/agent-skills/peerlink/SKILL.md`, indexed at `/.well-known/agent-skills/index.json`. Install it into agents that support skills.

## Good agent behaviour

- Confirm with the user before granting access to anyone, and before `delete_app`.
- After publishing, tell the user the URL **and** that nobody can open it until they choose who to invite.
- Never put API tokens, session cookies or secrets in app code, commits or chat output.
- If `publish` fails, read the error, run `project_doctor`, fix, and retry. Don't work around the platform's rules.
