---
title: "Installing the Cosigner"
description: "Install cosigner-cli, verify the signed runtime, pair with Fystack and start the cosigner."
url: https://docs.fystack.io/cosigner/installation
last_updated: 2026-10-14
---

> ## Documentation index
> Fetch the complete documentation index at: https://docs.fystack.io/llms.txt
> Use it to discover all available pages before exploring further.

# Installing the Cosigner

Regular mode on Linux with Docker. [Nitro Enclave](https://docs.fystack.io/cosigner/nitro-enclave.md) support is coming soon.

_Diagram: Install, verify, pair, connect_

## Prerequisites

- **Host**: Linux `amd64` or `arm64` for production. macOS or WSL 2 for development.
- **Docker**: Engine 20.10+ with Compose v2.
- **cosign**: v3+. The installer adds it if missing.
- **From Fystack**: a pairing token (`cps_...`) and your Apex endpoint.
- **Callback server**: required for production. See [Approval Callback](https://docs.fystack.io/cosigner/approval-callback.md).

**Network:** no inbound ports. Outbound HTTPS to Apex, the Fystack Gateway, `ghcr.io`, your callback and (unattended profile) AWS KMS.

---

## Step 1: Install cosigner-cli

```bash
curl -sSL https://cosigner.fystack.io/install.sh | sh
```

Checks the binary's SHA-256 and Sigstore signature, then installs to `/usr/local/bin` (or `~/.local/bin`).

| Option | Env var | Effect |
| --- | --- | --- |
| `--version vX.Y.Z` | `COSIGNER_VERSION` | Install a specific release |
| `--dir DIR` | `COSIGNER_INSTALL_DIR` | Install location |
| `--skip-docker-check` |  | Skip Docker checks |

> **Note:** To verify the installer before running it, see [Supply chain verification](https://docs.fystack.io/cosigner/security.md#supply-chain-verification).

## Step 2: Create the instance directory

```bash
export COSIGNER_HOME=/srv/cosigner
mkdir -p $COSIGNER_HOME/secrets && chmod 700 $COSIGNER_HOME/secrets
```

## Step 3: Write the config

Pick a profile:

- **`manual`** (default): storage key wrapped with a password. Enter it on every start. No auto-restart.
- **`unattended`**: storage key wrapped by your AWS KMS key. Starts on boot.

Keep the file outside the instance directory; `up` copies it in.

**config.yaml**

```yaml
storage:
  backend: sqlite
  sqlite:
    path: data/cosigner.db
  encryption:
    kek:
      type: password_envelope
      config:
        path: /run/secrets/storage-kek.enc
approval:
  require_callback: true
audit:
  type: file
  config:
    path: /var/lib/cosigner/audit.jsonl
callback:
  url: https://YOUR-CALLBACK-HOST/cosigner/approve
  timeout: 30s
  retry_count: 1
  private_key:
    type: file
    config:
      path: /run/secrets/cosigner-private.der.hex
  response_public_key_path: /run/secrets/server-public.pem
```

```yaml
storage:
  backend: sqlite
  sqlite:
    path: data/cosigner.db
  encryption:
    kek:
      type: kms
      config:
        provider: aws
        region: YOUR-AWS-REGION
        key_id: YOUR-KMS-KEY-ARN
        ciphertext_blob_path: /run/secrets/storage-kek.kms
approval:
  require_callback: true
audit:
  type: file
  config:
    path: /var/lib/cosigner/audit.jsonl
callback:
  url: https://YOUR-CALLBACK-HOST/cosigner/approve
  timeout: 30s
  retry_count: 1
  private_key:
    type: file
    config:
      path: /run/secrets/cosigner-private.der.hex
  response_public_key_path: /run/secrets/server-public.pem
```

- `/var/lib/cosigner` maps to `$COSIGNER_HOME/state`; `/run/secrets` maps to `$COSIGNER_HOME/secrets` (read-only).
- **Unattended**: encrypt a random 32-byte key with your KMS key, save the base64 ciphertext as `secrets/storage-kek.kms`. The container needs `kms:Decrypt` via an IAM role.
- **Callback keys**: place them in `secrets/` (mode `600`), or run `cosigner-cli callback add` after setup.

> **Note:** Without `--config`, `up` writes a dev-only default with `require_callback: false`. Always pass a config in production.

## Step 4: Check the host

```bash
cosigner-cli doctor
```

Checks Docker, cosign and config. Reports `unconfigured`, `configured but unpaired` or `paired`.

## Step 5: Pair and start

```bash
cosigner-cli up \
  --apex-endpoint https://YOUR-APEX-HOST \
  --config /secure/config.yaml
```

Add `--profile unattended` for KMS. `up` then:

1. **Verifies** the newest `ghcr.io/fystack/cosigner` release with cosign and pins its digest, before asking for any secret.
2. **Prompts** for the storage password (manual) and pairing token. Use `--token-file` to pass the token non-interactively.
3. **Pairs**: generates an Ed25519 identity, registers it with Apex, saves `state/pairing.yaml`.
4. **Starts** the container, unlocks it and connects to the Gateway.

Pin a release with `--image ghcr.io/fystack/cosigner@sha256:...`. Digests are on [cosigner.fystack.io](https://cosigner.fystack.io).

## Step 6: Verify

```bash
cosigner-cli status
cosigner-cli info
cosigner-cli logs -f
```

Once `status` shows ready, the cosigner is online and can join keygen.

## Instance layout

| Path | Contents |
| --- | --- |
| `cosigner-cli.yaml` | Apex endpoint, pinned image digest, profile |
| `config.yaml` | Runtime config (read-only) |
| `docker-compose.yaml` | Generated, hardened Compose file |
| `secrets/` | Wrapped storage key and callback keys |
| `state/pairing.yaml` | Pairing record from Apex |
| `state/cosigner.key` | Ed25519 identity key |
| `state/data/cosigner.db` | Encrypted key shares and session state |
| `state/audit.jsonl` | Signed audit log |

## Run on boot

Unattended profile only. Adapt the bundled systemd unit:

```ini
[Unit]
Description=Fystack Cosigner (KMS unattended profile)
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
User=cosigner
Group=cosigner
SupplementaryGroups=docker
ExecStart=/usr/local/bin/cosigner-cli --home /var/lib/fystack-cosigner start
ExecStop=/usr/local/bin/cosigner-cli --home /var/lib/fystack-cosigner stop
TimeoutStartSec=360
TimeoutStopSec=60
UMask=0077

[Install]
WantedBy=multi-user.target
```

For the manual profile, pass the password through a file descriptor:

```bash
COSIGNER_PASSWORD_FD=3 cosigner-cli start 3< /run/secrets/cosigner-pw
```

## Troubleshooting

| Symptom | Fix |
| --- | --- |
| Image signature verification failed | Don't bypass. Check the image reference and contact security@fystack.io. |
| identity already registered | Restore the original pairing data from backup instead of re-pairing. |
| Exits right after start | Startup callback ping failed. Check the callback is reachable and trusts `cosigner-public.pem`. |
| AlreadyExists | Another instance with this identity is connected. Stop it. |
| PermissionDenied | Cosigner was revoked. It will not reconnect. |
| Not ready after first start | Generating ECDSA pre-parameters. Wait up to 5 minutes. |
