> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockops.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Install on Linux hosts (systemd)

> Deploy a production citadel-core cluster as hardened systemd services on three or more Linux hosts.

This guide installs citadel-core as a systemd service on each host of a three-node cluster. It is the production path for bare-metal or virtual-machine deployments; for containers see [Kubernetes](/self-hosted/install-kubernetes) or [ECS](/self-hosted/install-ecs).

## Prerequisites

* **At least three Linux hosts** with network connectivity between them. ARM hosts are supported.
* A production **NATS** cluster with JetStream, TLS and credentials, reachable from every host.
* A production **Consul** cluster reachable from every host.
* `git` and `mkcert` (or your own CA) for TLS material; `age` if you encrypt identity keys.
* The citadel-core binaries from Blockops, installed to `/usr/local/bin/citadel-core` and `/usr/local/bin/citadel-core-cli`.

<Warning>
  Every command below that generates key material (peers, identities, the initiator key, passwords, the chain code) produces secrets that protect customer funds. Run them on a secure workstation, store the outputs in your secrets manager, and never leave copies in shell history or shared directories.
</Warning>

## Install

<Steps>
  <Step title="Create the service user and directories (every host)">
    ```bash theme={null}
    sudo useradd -r -s /bin/false -d /opt/citadel-core -c "citadel-core MPC node" citadel-core
    sudo mkdir -p /opt/citadel-core /etc/citadel-core

    # Application data: service-owned
    sudo chown -R citadel-core:citadel-core /opt/citadel-core
    sudo chmod 750 /opt/citadel-core
    sudo chmod g+s /opt/citadel-core

    # Configuration: root-controlled, service-readable
    sudo chown root:citadel-core /etc/citadel-core
    sudo chmod 750 /etc/citadel-core
    ```
  </Step>

  <Step title="Generate the peer registry (one host only)">
    ```bash theme={null}
    cd /opt/citadel-core
    citadel-core-cli generate-peers -n 3
    ```

    This writes `peers.json`, mapping `node0`, `node1` and `node2` to random IDs. Copy the same file to `/opt/citadel-core/peers.json` on every host; the registry must be identical everywhere.
  </Step>

  <Step title="Generate the event initiator key (one host only)">
    ```bash theme={null}
    citadel-core-cli generate-initiator --encrypt
    ```

    This produces `event_initiator.identity.json` (public key) and `event_initiator.key.age` (encrypted private key). The private key goes to the system that initiates MPC operations, citadel-api. The public key goes into every node's configuration in the next step.
  </Step>

  <Step title="Write the configuration (every host)">
    Copy the production template to `/etc/citadel-core/config.yaml` and set:

    * `nats.url`, `nats.username`, `nats.password` and the `nats.tls` certificate paths
    * `consul.address` and its credentials
    * `mpc_threshold`: `2` for a three-node cluster
    * `event_initiator_pubkey`: the `public_key` from `event_initiator.identity.json`
    * `chain_code`: one value generated with `openssl rand -hex 32`, **identical on every node**
    * `environment: production`

    Then lock the file down:

    ```bash theme={null}
    sudo chown root:citadel-core /etc/citadel-core/config.yaml
    sudo chmod 640 /etc/citadel-core/config.yaml
    ```

    The [configuration reference](/self-hosted/configuration) explains each key.
  </Step>

  <Step title="Register peers and generate the node identity (every host)">
    ```bash theme={null}
    citadel-core-cli register-peers --config /etc/citadel-core/config.yaml --environment production
    citadel-core-cli generate-identity --node node0 --encrypt   # node1, node2 on the other hosts
    ```

    `generate-identity` writes `identity/node0_identity.json` and `identity/node0_private.key.age`. Copy **every node's `*_identity.json`** (never the private key) into `/opt/citadel-core/identity/` on every host, so each node can verify its peers.
  </Step>

  <Step title="Install TLS certificates">
    Place the NATS client certificate, key and CA under `/opt/citadel-core/certs/` (`client-cert.pem`, `client-key.pem`, `rootCA.pem`) and reference them from `nats.tls` in the configuration.
  </Step>

  <Step title="Provide the share-store password">
    The share store is encrypted under a password that is supplied to the service through systemd's encrypted credentials, not written to the configuration file. Blockops' `setup-citadel-core-cred.sh` prompts for the password and seals it for the unit.

    <Warning>
      Back the password up in your secrets manager before starting the node. The share store cannot be opened without it, and it cannot be changed after the database is created.
    </Warning>
  </Step>

  <Step title="Install and start the service">
    ```bash theme={null}
    sudo ./setup-config.sh
    sudo systemctl status citadel-core
    journalctl -f -u citadel-core
    ```

    A healthy start logs, in order: the version banner, `Connected to badger kv store`, `Loaded peers from consul`, `[READY] Node is ready`, `Starting consumers`.
  </Step>
</Steps>

## The service unit

The unit installed by `setup-config.sh` runs the node as the unprivileged `citadel-core` user with the configuration directory read-only and the data directory writable, and applies systemd hardening:

```ini theme={null}
[Service]
User=citadel-core
Group=citadel-core
WorkingDirectory=/opt/citadel-core
ExecStart=/usr/local/bin/citadel-core start --name ${CITADEL_CORE_NODE_NAME} \
  --config /etc/citadel-core/config.yaml \
  --password-file=%d/citadel-core-db-password.cred
EnvironmentFile=/opt/citadel-core/.env
Restart=always
RestartSec=20

NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=full
ProtectHome=yes
ReadOnlyPaths=/etc/citadel-core
ReadWritePaths=/opt/citadel-core
CapabilityBoundingSet=CAP_DAC_READ_SEARCH
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
```

`CITADEL_CORE_NODE_NAME` is set in `/opt/citadel-core/.env` on each host (`node0`, `node1`, `node2`).

## Resulting layout

```text theme={null}
/opt/citadel-core/            application home (citadel-core:citadel-core, 750)
├── certs/                    NATS TLS material
├── db/                       encrypted share store (created on first start)
├── backups/                  encrypted backups (created automatically)
├── identity/                 every node's *_identity.json + this node's private key
├── peers.json
└── .env                      CITADEL_CORE_NODE_NAME

/etc/citadel-core/            configuration (root:citadel-core, 750)
└── config.yaml               (root:citadel-core, 640)
```

## Next

Continue with [Operate a cluster](/self-hosted/operate) for health checks, backups, upgrades and recovery, and confirm the [security checklist](/self-hosted/security-checklist) before the cluster holds production shares.
