# ac12.dev — CLI & Agent Skill

ac12.dev is a self-hosted PaaS for deploying Docker containers, managing
domains, files, cron jobs, email accounts, databases, git repositories, and AI
coding agents.

## Installation

Copy this file and the CLI binary into your agent's skill directory. The
expected layout is:

```
<skill-dir>/
  SKILL.md        ← this file
  scripts/
    ac12           ← CLI binary
```

### Cursor

```bash
mkdir -p .cursor/skills/ac12dev/scripts
curl -fsSL https://ac12.dev/SKILL.md -o .cursor/skills/ac12dev/SKILL.md
URL="https://cli.p.ac12.dev/ac12dev-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
curl -fsSL "$URL" -o .cursor/skills/ac12dev/scripts/ac12
chmod +x .cursor/skills/ac12dev/scripts/ac12
```

### Claude Code

```bash
mkdir -p .claude/skills/ac12dev/scripts
curl -fsSL https://ac12.dev/SKILL.md -o .claude/skills/ac12dev/SKILL.md
URL="https://cli.p.ac12.dev/ac12dev-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
curl -fsSL "$URL" -o .claude/skills/ac12dev/scripts/ac12
chmod +x .claude/skills/ac12dev/scripts/ac12
```

Or append to your project's `CLAUDE.md` for always-on context:
`curl -fsSL https://ac12.dev/SKILL.md >> CLAUDE.md`

### Other agents

```bash
mkdir -p ac12dev/scripts
curl -fsSL https://ac12.dev/SKILL.md -o ac12dev/SKILL.md
URL="https://cli.p.ac12.dev/ac12dev-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
curl -fsSL "$URL" -o ac12dev/scripts/ac12
chmod +x ac12dev/scripts/ac12
```

### Direct download links

| Platform                    | URL                                           |
| --------------------------- | --------------------------------------------- |
| macOS ARM64 (Apple Silicon) | `https://cli.p.ac12.dev/ac12dev-darwin-arm64` |
| macOS AMD64 (Intel)         | `https://cli.p.ac12.dev/ac12dev-darwin-amd64` |
| Linux AMD64                 | `https://cli.p.ac12.dev/ac12dev-linux-amd64`  |

## CLI Usage

The `ac12` binary lives at `scripts/ac12` relative to this skill file. **Always
invoke it using the full path from the workspace root.** For example, if this
skill is at `.cursor/skills/ac12dev/SKILL.md`, the CLI is at
`.cursor/skills/ac12dev/scripts/ac12`.

In all examples below, `ac12` refers to this full path — **always expand it**.

**Tip:** To keep commands clean, export the path for the current session:

```bash
export PATH="$(pwd)/.cursor/skills/ac12dev/scripts:$PATH"
```

## Profiles

The CLI supports credential profiles so multiple agents on the same machine
don't conflict. Each profile is stored at `~/.ac12/profiles/<name>.json`.

**Profile resolution order** (first match wins):

1. `--profile <name>` flag
2. `AC12_PROFILE` env var
3. `.ac12profile` file in the current directory (walks up to root) — contains
   just the profile name
4. `~/.ac12/credentials.json` (legacy fallback)

After registration or login, pin the profile to the current directory:

```bash
ac12 profile use <name>
```

This writes a `.ac12profile` file. All `ac12` commands run from this directory
(or subdirectories) will use that profile automatically.

```bash
ac12 profile list               # list all profiles (* = active)
ac12 profile show [name]        # show active profile details
ac12 profile use <name>         # write .ac12profile in current dir
ac12 profile delete <name>      # remove a profile
```

## Registration (One-Time Setup)

Registration is invite-only. You need a **referral link** from an existing admin
to create an account.

### Step 1: Register with a referral code

```bash
ac12 --profile <name> auth register <username> <email> --server https://ac12.dev --referral <code>
ac12 profile use <name>
```

You can also register via the web by opening the referral URL directly:
`https://ac12.dev?referral=<code>`

This generates an Ed25519 keypair, registers the account, and saves credentials.
The verification code is returned in the response. Each referral code is
single-use and may have an expiration date.

### Step 2: Verify email

```bash
ac12 auth verify-email <code>
```

If you didn't receive the code, resend it: `ac12 auth resend-code`

### Step 3: Complete verification challenge

After email verification, complete the conversation challenge (5 rounds):

```bash
ac12 auth challenge "your response message"
```

### Step 4: Confirm

```bash
ac12 auth whoami
```

You should see `Verified: true`.

### Login (existing account)

```bash
ac12 --profile <name> auth login <username> <key_file> --server https://ac12.dev
ac12 profile use <name>
```

### Export keys

```bash
ac12 auth export-key            # print private key
ac12 auth export-key --public   # print public key
```

## Project Management

Every resource belongs to a project. After registration, you have a default
project.

```bash
ac12 project list                           # list all projects (* = active)
ac12 project create <name> [-d DESC] [--use]
ac12 project get [--project ID]
ac12 project use <project_id>               # switch active project
ac12 project update [--name N] [-d DESC]
ac12 project delete <project_id>
```

### Members

```bash
ac12 project members
ac12 project add-member <username> [--role member]
ac12 project update-member <user_id> <role>
ac12 project remove-member <user_id>
```

## Services

Deploy and manage Docker containers.

```bash
ac12 service list
ac12 service create <name> <image> [-p PORT] [-e KEY=VAL] [--no-daemon] [--no-deploy]
ac12 service get <name>
ac12 service update <name> [--image IMG] [-p PORT] [-e KEY=VAL] [--daemon]
ac12 service deploy <name> [--image IMG] [-e KEY=VAL]
ac12 service logs <name> [-n 100] [--since TIMESTAMP]
ac12 service start <name>
ac12 service stop <name>
ac12 service restart <name>
ac12 service delete <name>
ac12 service deployments <name>
```

## Registry

Private Docker image registry. Two ways to push images:

**Option A — Pull from a remote registry** (image must be publicly pullable):

```bash
ac12 registry push docker.io/library/nginx:alpine my-nginx --tag latest
```

The `source` must be a **fully-qualified remote image reference** (e.g.
`docker.io/library/nginx:alpine`, `ghcr.io/org/image:tag`). This does NOT work
with local image names — `ac12 registry push myapp:latest myapp` will fail
because the server tries to pull from a remote registry.

**Option B — Upload a locally-built image as a tar** (recommended for custom
images):

```bash
docker build -t myapp:latest .
docker save myapp:latest -o /tmp/myapp.tar
ac12 registry push-tar /tmp/myapp.tar myapp --tag latest
rm /tmp/myapp.tar
```

**Important:** The platform host runs **Linux AMD64**. If you're building on a
different architecture (e.g. macOS ARM64), you must cross-build:

```bash
docker build --platform linux/amd64 -t myapp:latest .
```

Other registry commands:

```bash
ac12 registry list
ac12 registry get <name>
ac12 registry delete <name>
ac12 registry delete-tag <name> <tag>
```

## Domains

Map `<subdomain>.p.ac12.dev` to a service or public file path.

```bash
ac12 domain list
ac12 domain create <subdomain> --type service --service <name>
ac12 domain create <subdomain> --type file --path <path>
ac12 domain update <domain_id> [--type T] [--service S] [--path P]
ac12 domain delete <domain_id>
```

## Files

Upload and manage files. All files are automatically version-controlled via git.
Public files are served at `https://ac12.dev/f/{project_id}/{path}`.

```bash
ac12 file list [--prefix PREFIX]
ac12 file upload <local_file> <remote_path> [--public]
ac12 file get <path> [-o output_file] [--download]
ac12 file cat <path>
ac12 file edit <path> [--content-file FILE]
ac12 file delete <path>
ac12 file log
```

## Cron Jobs

Schedule HTTP requests to your deployed services.

```bash
ac12 cron list
ac12 cron create <name> "<schedule>" <service> [--path /] [--method GET] [--body JSON]
ac12 cron get <job_id>
ac12 cron update <job_id> [--name N] [--schedule S] [--service S] [--path P] [--method M] [--enable] [--disable]
ac12 cron delete <job_id>
ac12 cron trigger <job_id>
ac12 cron runs <job_id>
```

## Database

Each project gets a SQLite database.

```bash
ac12 db info
ac12 db query "SELECT * FROM users LIMIT 10" [param1 param2...]
ac12 db execute "INSERT INTO users (name) VALUES (?)" "Alice"
```

## Email

Manage `@ac12.dev` email accounts.

```bash
ac12 email list
ac12 email create <address> [--display-name NAME]
ac12 email get <account_id>
ac12 email delete <account_id>
ac12 email inbox <account_id> [--page N] [--per-page N]
ac12 email read <account_id> <message_id>
ac12 email delete-message <account_id> <message_id>
ac12 email send <account_id> --to ADDR --subject "Subject" --body "Body" [--cc ADDR]
ac12 email reply <account_id> <message_id> --body "Reply body"
ac12 email sent <account_id> [--page N]
```

## Agents

AI coding agents that modify repo code and deploy via the platform.

```bash
ac12 agent create <name> [--model MODEL] [--system-prompt TEXT]
ac12 agent list
ac12 agent get <name>
ac12 agent update <name> [--model MODEL] [--system-prompt TEXT]
ac12 agent delete <name>
ac12 agent run <name> "task description" [--branch BRANCH]
ac12 agent runs <name>
ac12 agent logs <name> <run_id>
ac12 agent cancel <name> <run_id>
```

## Deploying from Inside an Agent Container

When running as an ac12dev agent (inside the agent container), you have direct
access to the Docker socket and the ac12 CLI. You do NOT need rsync or SSH. Use
this workflow to build and deploy services:

### New service (first time)

```bash
# 1. Build the image
docker build -t myapp:latest ./path/to/app/

# 2. Export and push to the registry
docker save myapp:latest -o /tmp/myapp.tar
ac12 registry push-tar /tmp/myapp.tar myapp --tag latest
rm /tmp/myapp.tar

# 3. Create the service and domain
ac12 service create myapp myapp:latest --port 8000
ac12 domain create myapp --type service --service myapp

# 4. Deploy
ac12 service deploy myapp
```

### Updating an existing service

```bash
docker build -t myapp:latest ./path/to/app/
docker save myapp:latest -o /tmp/myapp.tar
ac12 registry push-tar /tmp/myapp.tar myapp --tag latest
rm /tmp/myapp.tar
ac12 service deploy myapp
```

Skip the `service create` and `domain create` steps — they only need to run
once.

### How to tell if you're inside an agent container

If the Docker socket is available at `/var/run/docker.sock` and there is no SSH
key configured, you are inside the agent container. Use the workflow above
instead of rsync/SSH.

## Deployment Best Practices

- **Always use a `.dockerignore`** to exclude build artifacts, tar files, and
  the `scripts/` directory from your Docker build context. This prevents slow
  builds and bloated images:

  ```
  scripts/
  *.tar
  .venv/
  __pycache__/
  .git/
  ```

- **Cross-build for the platform** — the host is Linux AMD64. If building on
  macOS ARM64, use `docker build --platform linux/amd64`.

- **Clean up tar files** after `push-tar` — they can be large and will slow down
  future builds if left in the working directory.

## Global Flags

```
--profile <name>   Use a specific credential profile
--project <id>     Override the active project for this command
```

## Auth Mechanism

Ed25519 request signing. Every authenticated request includes:

- `X-Username`: your username
- `X-Timestamp`: Unix timestamp (must be within 60s of server time)
- `X-Signature`: base64 Ed25519 signature of
  `{METHOD}\n{PATH}\n{TIMESTAMP}\n{SHA256(body)}`

The CLI handles signing automatically.

## Architecture

```
Internet → Caddy (TLS, *.p.ac12.dev) → platform-api (FastAPI)
                                     → service containers
                                     → registry (internal)
                                     → maddy (SMTP/IMAP)
                                     → gitea (internal git)
                                     → agent containers (ephemeral)
```

- Services proxied at: `https://ac12.dev/p/{project_id}/{service_name}/{path}`
- Subdomains: `https://{name}.p.ac12.dev/{path}`
- Public files: `https://ac12.dev/f/{project_id}/{path}`
