---
title: "Register a new Daemon"
method: POST
path: "/api/daemons/register"
tags: ["Daemons", "internal"]
---

# Register a new Daemon

`POST /api/daemons/register`

Internal endpoint for daemon self-registration. Creates a host entry
and sets up default discovery jobs for the daemon.

## Request body

- DaemonRegistrationRequest — Daemon registration request from daemon to server
  - `capabilities` LegacyCapabilities — Legacy inbound-only capabilities blob. Pre-0.15 daemons report their interfaced subnets as bare `subnet_id`s in this `capabilities` object (they predate the `interfaced_subnets: Vec<Subnet>` heartbeat channel). It is deserialize-only: the server never stores it, never echoes it in `DaemonResponse`, and it has no `SqlValue` variant. Reported ids are routed into the `daemon_interfaced_subnets` junction (existence-filtered) so legacy daemons keep reporting interfaced subnets. ≥0.15 daemons send the `Vec<Subnet>` channel instead and leave this empty.
    - `interfaced_subnet_ids` string[], required — Subnets the daemon has an interface on, as reported by older daemons.
  - `daemon_id` string, uuid, required — The daemon this entity refers to.
  - `integration_targets` IntegrationTarget[] — Per-daemon integration targeting from the init command (credentialed cred↔IP and credential-less local sockets). Written to this daemon's Discovery at registration so it's present before the first session dispatches. Registration assumes new-daemon → new-server, so there is no legacy bare-`credential_ids` field — bare-uuid env back-compat is handled in the daemon's env parser, never on the wire.
    - union — Per-daemon integration targeting, stored on the `Discovery` entity and delivered via the init command at registration. Each entry references exactly one stored credential and says where it applies on this daemon. This is the single home for cred↔IP targeting — it replaces the global, race-prone `credential.target_ips`. The variants ARE the scopes; their strum [`Target`] discriminants are the capability enum that `CredentialType::targets()` returns and validates against (single source of truth). Every target carries a real `credential_id` — there is no credential-less branch and no nil sentinel; a local socket is just a credential whose type targets only the daemon host.
      - object — The daemon's own host — realized as a 127.0.0.1 IP-override (e.g. a local Docker/Podman socket, or any credential the user pins to the daemon host without naming its IP).
        - `credential_id` string, uuid, required — Credential to use on the daemon host.
        - `scope` 'DaemonHost', required
      - object — All hosts on the network — a broadcast default credential.
        - `credential_id` string, uuid, required — Credential to use across the network.
        - `scope` 'Network', required
      - object — Specific host IPs — one IP-override per address.
        - `credential_id` string, uuid, required — Credential to use on the listed addresses.
        - `ips` string[], required — The host addresses this credential applies to.
        - `scope` 'Hosts', required
  - `mode` 'server_poll' | 'daemon_poll', required — Daemon operating mode that determines the communication pattern. - **DaemonPoll** (formerly "Pull"): Daemon makes outbound connections to the server. The daemon registers itself and polls for work. Best for daemons behind NAT/firewall. - **ServerPoll** (formerly "Push"): Server makes connections to the daemon. Server polls daemon for status and discovery results. Best for DMZ deployments where daemon cannot make outbound connections.
  - `name` string, required — Name the daemon reports for itself.
  - `network_id` string, uuid, required — The network this entity belongs to.
  - `url` string, nullable — URL is ignored by server - kept for backwards compat with old daemons. URL is only set via admin provisioning for ServerPoll daemons.
  - `user_id` string, uuid — User responsible for maintaining this daemon (from frontend install command) Optional for backwards compat with old daemons - defaults to nil UUID
  - `version` string, nullable — Daemon software version (optional for backwards compat with old daemons)

## Response `200`

Daemon registered successfully

- ApiResponseDaemonRegistrationResponse
  - `data` object — Daemon registration response from server to daemon
    - `daemon` Daemon, required
      - `api_key_id` string, uuid, nullable — Foreign key to API key used for ServerPoll authentication. NULL for DaemonPoll daemons or those not yet linked to a key.
      - `host_id` string, uuid, required — The host this entity belongs to.
      - `is_unreachable` boolean — Whether the daemon is unreachable (for ServerPoll circuit breaker). Set to true after repeated polling failures, reset via retry-connection endpoint.
      - `last_seen` string, date-time, nullable — Timestamp of last successful contact with daemon. NULL for provisioned ServerPoll daemons that haven't been contacted yet.
      - `mode` 'server_poll' | 'daemon_poll', required — Daemon operating mode that determines the communication pattern. - **DaemonPoll** (formerly "Pull"): Daemon makes outbound connections to the server. The daemon registers itself and polls for work. Best for daemons behind NAT/firewall. - **ServerPoll** (formerly "Push"): Server makes connections to the daemon. Server polls daemon for status and discovery results. Best for DMZ deployments where daemon cannot make outbound connections.
      - `name` string, required — Human-facing name for this daemon.
      - `network_id` string, uuid, required — The network this entity belongs to.
      - `standby` boolean — Whether the daemon is on standby due to inactivity (no discovery in 30 days).
      - `standby_cleared_at` string, date-time, nullable — Timestamp of the most recent standby → active transition. Set by `process_startup` when a restarted daemon is un-standby'd, and by the discovery auto-wake path. The nightly inactivity check skips daemons within the grace window (see `STANDBY_GRACE_PERIOD_DAYS`) to prevent the "restart → cleared → re-standby'd before discovery runs" race.
      - `tags` string[], required — Tags assigned to this entity.
      - `url` string, uri, required — Address the *server* dials for a ServerPoll daemon. Editable (a daemon can move); unused and not editable for DaemonPoll, which dials out instead. Base URL the server reaches this daemon on.
      - `user_id` string, uuid, required — User responsible for maintaining this daemon
      - `version` string, nullable — Daemon software version (semver format)
      - `created_at` string, date-time, required — When this record was first created.
      - `id` string, uuid, required — Server-assigned unique identifier.
      - `updated_at` string, date-time, required — When this record was last modified.
    - `host_id` string, uuid, required — The host this entity belongs to.
    - `server_capabilities` ServerCapabilities — Server capabilities returned on startup/registration
      - `deprecation_warnings` DeprecationWarning[] — Deprecation warnings for the daemon
        - `message` string, required — What the operator needs to do, and by when.
        - `severity` 'Info' | 'Warning' | 'Critical' | 'Unknown', required — Severity level for deprecation warnings
        - `sunset_date` string, nullable — Date after which this daemon version stops being supported.
      - `minimum_daemon_version` string, required — Minimum daemon version supported by this server
      - `server_version` string, required — Server software version
  - `error` string, nullable — Human-readable failure message. Omitted on success.
  - `meta` ApiMeta, required — API metadata included in all responses
    - `api_version` integer, required — API version (integer, increments on breaking changes)
    - `server_version` string, required — Server version (semver)
  - `success` boolean, required — `true` when the request succeeded. `false` responses carry `error` instead of `data`.

## Other responses

- `403` — Daemon registration disabled in demo mode

---

[API](https://skmtc.net/scanopy/apis/scanopy-api.md) · [All operations](https://skmtc.net/scanopy/apis/scanopy-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/scanopy/scanopy-api/revisions/2df1fc47dfa7/schema)
