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

# Get started with Certplane

> Deploy the broker, enroll your first host with a bootstrap token, and obtain a public TLS certificate end-to-end in a single workflow.

This guide walks you through the full Certplane setup: running the broker, defining a certificate policy, enrolling a host with its identity certificate, and starting the agent renewal loop. By the end, your host will have a publicly trusted TLS certificate that renews automatically.

<Tip>
  Prefer Ansible? Skip ahead to the [Ansible roles guide](/guides/ansible) — the `certplane_broker` and `certplane_agent` roles automate every step on this page.
</Tip>

<Steps>
  <Step title="Prerequisites">
    Before you begin, make sure you have the following in place:

    * **step-ca** — A running [step-ca](https://smallstep.com/docs/step-ca/) instance reachable by your hosts. This is the internal CA that issues agent identity certificates.
    * **Let's Encrypt access** — The broker uses ACME to obtain public certificates. You need a valid email address for the ACME account.
    * **A DNS provider supported for dns-01** — Currently `cloudflare` or `httpreq`. Wildcard profiles require `dns-01`.
    * **The Certplane binaries** — `certplane-broker` on your broker host and `certplane-agent` on each managed host. Build with `make` from the [source repo](https://github.com/TaconeoMental/Certplane).
  </Step>

  <Step title="Deploy the broker">
    The broker is the central server that enforces policy and obtains certificates from your public CA. Create `/etc/certplane/broker.yml` (this mirrors [`examples/config/broker.yml`](https://github.com/TaconeoMental/Certplane/blob/main/examples/config/broker.yml)):

    ```yaml broker.yml theme={null}
    server:
      address: ":8443"
      tls:
        cert: /etc/certplane/broker/broker.crt
        key: /etc/certplane/broker/broker.key
        min_version: "1.2"
      mtls:
        agent_ca_bundle: /etc/certplane/ca/agent-identity-ca-bundle.crt
      read_header_timeout: 5s
      read_timeout: 10s
      write_timeout: 60s
      idle_timeout: 120s

    policy:
      path: /etc/certplane/policy.yml
      watch: false

    issuer:
      provider: acme
      acme:
        directory_url: https://acme-v02.api.letsencrypt.org/directory
        account_email: admin@example.com
        account_key: /var/lib/certplane/acme/account.key
        dns_provider: cloudflare

    secrets:
      provider: env

    store:
      driver: sqlite
      path: /var/lib/certplane/broker.db

    audit:
      enabled: true
      failure_mode: fail_open
      mirror_to_log: true

    rate_limits:
      per_identity_per_hour: 50
      per_identity_profile_per_hour: 20

    logging:
      level: info
      format: json
      destination: stdout
    ```

    The `server.mtls.agent_ca_bundle` must be the CA bundle that signed (or will sign) your agent identity certificates — typically the `step-ca` root or intermediate.

    Start the broker:

    ```bash theme={null}
    certplane-broker -c /etc/certplane/broker.yml serve
    ```

    The broker listens on `:8443` by default and requires mTLS from every connecting agent. For the full configuration reference, see [Broker configuration](/configuration/broker).
  </Step>

  <Step title="Create a policy file">
    The policy file defines which certificate profiles exist and which hosts may request each one. Create `/etc/certplane/policy.yml`:

    ```yaml policy.yml theme={null}
    version: 1

    profiles:
      public_edge_main:
        type: wildcard
        dns_names:
          - "*.example.com"
        acme:
          challenge: dns-01
          credentials: cloudflare/example-com
        renew_before: 720h

    hosts:
      edge01:
        identity: edge01.h.int.example.com
        profiles:
          - public_edge_main
    ```

    * **profiles** — Each named profile sets `type` (`wildcard` or `multi_san`), allowed `dns_names`, ACME settings (`acme.challenge` and `acme.credentials`), and an optional `renew_before` window.
    * **hosts** — Each entry maps a label to an `identity` (the CN of the identity certificate issued by `step-ca`) and lists `profiles` the host may request. Requests for any other profile are rejected.

    Validate before you deploy it:

    ```bash theme={null}
    certplane-broker -c /etc/certplane/broker.yml policy validate --policy /etc/certplane/policy.yml
    ```

    For the full reference, see [Policy overview](/policy/overview).
  </Step>

  <Step title="Enroll your first host">
    Enrollment gives a host its identity certificate. The agent contacts `step-ca` once using a short-lived bootstrap token, generates an ECDSA key pair locally, and stores the resulting `identity.crt`.

    **Generate a bootstrap token** on your `step-ca` instance:

    ```bash theme={null}
    step ca token edge01.h.int.example.com
    ```

    Write the token to the host at the path referenced in the agent config (e.g., `/etc/certplane/bootstrap-token`, mode `0600`).

    **Create the agent config** at `/etc/certplane/agent.yml` (mirrors [`examples/config/agent.yml`](https://github.com/TaconeoMental/Certplane/blob/main/examples/config/agent.yml)):

    ```yaml agent.yml theme={null}
    state_dir: /var/lib/certplane/agent

    identity:
      name: edge01.h.int.example.com
      provider: step-ca
      cert: /var/lib/certplane/agent/identity.crt
      key: /var/lib/certplane/agent/identity.key
      issuer_ca_bundle: /etc/certplane/ca/agent-identity-ca-bundle.crt
      bootstrap_token: /etc/certplane/bootstrap-token
      renew_before: 8h
      warn_before: 24h
      step_ca:
        url: https://ca.int.example.com
        fingerprint: "sha256:aabbcc..."
        root_ca_bundle: /etc/certplane/ca/step-ca-root.crt
        timeout: 10s

    broker:
      url: https://certplane-broker.int.example.com:8443
      server_ca_bundle: /etc/certplane/ca/broker-server-ca-bundle.crt
      timeout: 30s

    logging:
      level: info
      format: text
      destination: stderr

    certificates:
      - name: edge-wildcard
        profile: public_edge_main
        dns_names:
          - "*.example.com"
        key:       /var/lib/certplane/agent/service.key
        cert:      /var/lib/certplane/agent/service.crt
        chain:     /var/lib/certplane/agent/service.chain.crt
        fullchain: /var/lib/certplane/agent/service.fullchain.crt
        renew_before: 720h
        reload_command: "systemctl reload traefik"
        reload_timeout: 30s
    ```

    **Run enrollment:**

    ```bash theme={null}
    certplane-agent -c /etc/certplane/agent.yml enroll
    ```

    The agent generates `identity.key` locally, creates a CSR, and submits it to `step-ca` along with the bootstrap token. On success, `identity.crt` is written and the bootstrap token file is deleted.

    <Note>
      The bootstrap token is consumed during enrollment and cannot be reused. If enrollment fails and you need to retry, generate a new token from `step-ca`. See [Agent enrollment](/setup/agent-enroll) for troubleshooting.
    </Note>
  </Step>

  <Step title="Run the agent">
    Once the host is enrolled, run the renewal loop. The agent is a one-shot binary — run it on a timer (the Ansible role ships a `systemd` timer; see [Running the agent](/setup/agent-run) for the standalone unit).

    ```bash theme={null}
    certplane-agent -c /etc/certplane/agent.yml run
    ```

    On each run, the agent:

    1. Renews the identity certificate if it expires within `identity.renew_before`.
    2. For each entry under `certificates`:
       * Ensures a local ECDSA service key exists at `key` (generated if missing, reused otherwise).
       * Skips the certificate if a valid one exists and is not yet within `renew_before` of expiry.
       * Otherwise builds a CSR with the configured `dns_names`, calls the broker over mTLS, validates that the returned bundle matches the local key and requested DNS names, writes `cert`, `chain`, and `fullchain`, and runs `reload_command` if set.

    Validate config and on-disk state without making changes:

    ```bash theme={null}
    certplane-agent -c /etc/certplane/agent.yml check
    ```
  </Step>
</Steps>

## Next steps

* [Ansible roles guide](/guides/ansible) — production deployment with the official roles.
* [Broker configuration](/configuration/broker) and [Agent configuration](/configuration/agent) — full schema reference.
* [Policy overview](/policy/overview) — profile and host model.
* [Let's Encrypt + dns-01](/guides/lets-encrypt-dns) — DNS provider credentials for wildcards.
