Architecture

How Palisade works

Palisade runs a pull-only loop between an on-host agent and the control plane. The agent enrolls once, discovers its own listening services, verifies a signed detection catalog, runs checks locally, and ships only normalized findings. The host opens no inbound ports and raw evidence never leaves the machine.

The loop

Eight stages, in order, from enrollment to scored posture.

  1. 1

    Enroll

    The agent enrolls once with a single-use token. Enroll mints the agent into an org and issues a client cert from an internal CA for mTLS. In plaintext demo mode it falls back to a bearer agent_secret.

  2. 2

    Heartbeat

    The agent polls the control plane for jobs. The model is pull-only: nothing is pushed to the host and no inbound ports are opened.

  3. 3

    Discover job

    The control plane issues a discover job. The agent enumerates listening services on-host and reports assets — service plus version.

  4. 4

    Scan job

    A heartbeat issues a scan job. Detections are matched to assets by service AND version range, so only relevant checks are dispatched.

  5. 5

    Pull + verify signed catalog

    The agent pulls the signed detection bundle, rebuilds the canonical manifest, and verifies the Ed25519 signature against a pinned public key. If verification fails it refuses to run any detection — fail closed.

  6. 6

    Run detections on-host

    Verified detections execute locally against the local services. Execution never leaves the host.

  7. 7

    Report findings

    Only normalized findings leave the host. Raw evidence stays local, and is AES-256-GCM encrypted per-org at rest if stored.

  8. 8

    Score + alert + triage

    The control plane scores posture with real 30-day trends, evaluates alert rules and delivers matching alerts in the background, and optionally AI-triages each finding off the request path.

Signed catalog bundles

The detection catalog ships as a bundle signed with Ed25519 over a canonical manifest. The agent rebuilds that manifest locally and verifies the signature against a pinned public key, so the catalog can travel over an untrusted channel without losing integrity.

The verification policy:

  • Empty signature — refuse to scan.
  • stub (no signing key configured) — proceed in dev mode with a warning.
  • Otherwise — verify the signature and refuse to run any detection on failure.

Version-aware matching

Detections match assets by service and version range. match.versions accepts comma- or space-separated constraints — for example <1.40.2 or >=11.1.4 <15.2.3.

A litellm <1.40.2 detection fires on 1.39.0 but not on 1.41.0, so checks are scoped to the versions actually affected.

Unknown or missing asset versions fail open — the detection is scanned anyway. A vulnerability is never silently skipped just because a version could not be determined.

Run it yourself (Quickstart)

Zero infra, sqlite-backed. Requires Go 1.22+, Python 3.12, and Node 18+.

Control plane

make venv
make migrate
cd control-plane && PALISADE_ENROLL_TOKENS=PLS-DEMO ./.venv/bin/uvicorn app.main:app --reload

Web UI

cd web && npm install && npm run dev

Or run it in Docker

cd control-plane && cp .env.example .env && docker compose up --build

Full loop smoke test

make smoke

See it end to end

Open the app to watch the loop produce posture and findings, or read the spec for the full design.