Fingerprint Runtime Agent
An observe-only gateway that sits between your application and an OpenAI-compatible endpoint. It builds a behavioural baseline, scores every exchange against it, enforces capability policy, and reports security telemetry to the CoreLayer control plane without touching model responses.
The corelayer-fingerprint-agent is a separate binary from the secureai CLI. It runs as a long-lived service rather than a one-shot command, and it is designed to sit on the inference path permanently. Your application sends its existing POST /v1/chat/completions requests to the agent instead of directly to the provider. The agent forwards each request to one fixed upstream, streams or returns the upstream response unchanged, and performs all scoring and telemetry work off the response path.
Nothing is blocked or rewritten
The agent does not block, rewrite, redact, retry, or choose model responses. If the agent process goes down or falls behind, the customer application loses observability, never inference. Policy detections and anomaly scores are telemetry, not authorization decisions.
How the agent works
On every request the agent performs the same sequence, entirely off the response path:
- Proxy. Forward the request to the fixed upstream and stream the response back to the caller, byte for byte. Header credentials pass through. The agent adds nothing to the response.
- Extract. Parse the request and response bodies (both streaming and non-streaming) into a structured inference record: model, messages, tool declarations, tool calls, token counts.
- Sessionize. Group the exchange into a pseudonymous session using
X-CoreLayer-Session-ID, the OpenAIuserfield, or a privacy-preserving connection fingerprint. Session IDs are HMAC-derived per process and never stored in cleartext. - Calibrate. During the calibration window the agent collects clean samples to build a behavioural baseline. Once the minimum sample count and duration are met, calibration activates and scoring begins.
- Score. Run the LBF (behavioural fingerprinting) engine against the session history. If the anomaly score crosses the alert threshold, emit a
behavior_anomalyevent. - Evaluate policy. Check request ceilings (tokens, message count, request size), tool authorization, capability boundaries (CBE), context access control (LCAC), and dangerous tool-call sequences. Every detection is observe-only.
- Spool and upload. Write the score sample and any events to an encrypted, crash-safe disk spool. A background worker delivers batches to the control plane with exponential backoff and idempotent retries.
Before you install
Installation is driven from the CoreLayer dashboard. Open the Runtime section, select the model you want to observe, and generate an installation revision. The dashboard produces:
- An
agent.yamlconfiguration file containing the non-secret identifiers, upstream URL, policy, and LCAC rules for this revision. - A one-time enrollment token. This token is short-lived and single-use. The agent exchanges it for a runtime credential on first startup, then the token is no longer needed.
Changing the content-capture setting or the policy creates a new immutable revision. Install that revision and let it enroll before retiring the previous one.
Docker Compose
The published container image runs as UID 65532 with every Linux capability dropped, a read-only root filesystem, bounded CPU and memory, and an HTTP health check. One persistent named volume holds the credential, calibration state, and encrypted spool.
cd deploy/fingerprint-agent
cp ../../secureai.agent.example.yaml agent.yaml
cp agent.env.example agent.env
chmod 0644 agent.yaml
chmod 0600 agent.env
# Paste the dashboard enrollment token into agent.env, then:
docker compose up -d
docker compose ps
curl -fsS http://127.0.0.1:8090/readyzThe published port binds to 127.0.0.1 by default. A sibling Compose service can reach the gateway at http://fingerprint-agent:8090/v1/chat/completions on the project network. Set CORELAYER_AGENT_BIND_IP only when another host must reach the gateway, and protect port 8090 with a firewall or private load balancer.
After the dashboard confirms enrollment, blank the token in agent.env and recreate the container. The runtime credential is stored in the named volume.
printf 'CORELAYER_AGENT_ENROLLMENT_TOKEN=\n' > agent.env
chmod 600 agent.env
docker compose up -d --force-recreateLinux binary and systemd
The install script downloads the versioned archive for your architecture, verifies it against the SHA-256 manifest, installs the binary to /usr/local/bin, and enables a hardened systemd unit. It runs as a locked corelayer-agent user with only the data directory writable.
curl -fsSLo /tmp/install-corelayer-agent.sh \
https://get.corelayersecurity.com/agent/install.sh
less /tmp/install-corelayer-agent.sh
# Save the dashboard token without putting it in a shell argument.
umask 077
read -r -s CORELAYER_AGENT_ENROLLMENT_TOKEN
printf '%s' "$CORELAYER_AGENT_ENROLLMENT_TOKEN" > /tmp/corelayer.token
unset CORELAYER_AGENT_ENROLLMENT_TOKEN
sudo CORELAYER_AGENT_CONFIG_FILE=/path/to/dashboard-agent.yaml \
CORELAYER_AGENT_ENROLLMENT_TOKEN_FILE=/tmp/corelayer.token \
sh /tmp/install-corelayer-agent.sh
rm -f /tmp/corelayer.tokenIf no dashboard config is provided, the installer writes a safe placeholder and leaves the service stopped. Edit /etc/corelayer/agent.yaml, then start it manually.
sudo systemctl status corelayer-fingerprint-agent
curl -fsS http://127.0.0.1:8090/healthz
curl -fsS http://127.0.0.1:8090/readyz
journalctl -u corelayer-fingerprint-agent --since "10 minutes ago"Once enrollment completes, remove the token assignment from /etc/corelayer/fingerprint-agent.env and restart. To pin a specific release or install without the service:
CORELAYER_AGENT_VERSION=0.1.0 \
CORELAYER_AGENT_INSTALL_DIR="$HOME/bin" \
CORELAYER_AGENT_SKIP_SERVICE=1 \
sh /tmp/install-corelayer-agent.shKubernetes and Helm
The Helm chart defaults to one replica because the local baseline, credential, and spool have a single writer. It enables a persistent volume, a read-only root filesystem, non-root UID/GID, dropped capabilities, seccomp RuntimeDefault, resource limits, probes, and checksum-triggered rollout on configuration changes.
Create the enrollment Secret out of band so it never enters a values file or Helm release history:
kubectl create namespace corelayer
kubectl -n corelayer create secret generic corelayer-agent-enrollment \
--from-literal=enrollment-token="$CORELAYER_AGENT_ENROLLMENT_TOKEN"
helm upgrade --install fingerprint-agent \
./deploy/fingerprint-agent/helm/corelayer-fingerprint-agent \
--namespace corelayer \
--set enrollment.existingSecret=corelayer-agent-enrollment \
--set image.tag=0.1.0The in-cluster gateway address is:
http://fingerprint-agent-corelayer-fingerprint-agent.corelayer.svc:8090/v1/chat/completionsAfter the dashboard shows a successful enrollment, upgrade the Deployment to remove its Secret reference, then delete the one-time Secret:
helm upgrade fingerprint-agent \
./deploy/fingerprint-agent/helm/corelayer-fingerprint-agent \
--namespace corelayer --reuse-values \
--set enrollment.existingSecret=""
kubectl -n corelayer delete secret corelayer-agent-enrollmentNetworkPolicy
networkPolicy.enabled is off by default because standard Kubernetes NetworkPolicy cannot express upstream and control-plane DNS names. When enabled, the included policy allows gateway ingress plus DNS and HTTPS egress. Narrow it with selectors or your CNI's FQDN rules. serviceMonitor.enabled renders a Prometheus Operator ServiceMonitor for /metrics.
Configuration reference
The agent reads a single strict YAML file at startup. Group-writable or world-writable files are rejected. The config is immutable after startup: capture_content cannot be changed by the control plane or any HTTP endpoint.
Top-level fields
| Field | Default | Description |
|---|---|---|
version | required | Must be 1. |
listen | 127.0.0.1:8090 | Address the gateway binds to. |
agent_id | required | Dashboard-generated agent identifier. |
model_id | required | Dashboard-generated model identifier. |
revision_id | required | Dashboard-generated revision identifier. |
capture_content | false | When true, redacted prompt and response snippets are included in anomaly and detection events. Changing this creates a new dashboard revision. |
upstream
| Field | Default | Description |
|---|---|---|
url | required | Fixed OpenAI-compatible Chat Completions endpoint. |
model | empty (any) | Expected provider model name (e.g. gpt-4o). A differing request model is reported as inventory drift. This is not the model_id above. |
timeout | 10m | Upstream request timeout. |
max_request_bytes | 8388608 | Hard request body ceiling. Requests above this fail. |
max_observation_bytes | 8388608 | How much of a response is held for analysis. |
observation_queue | 256 | Bounded queue between the response path and the processor. |
control_plane
| Field | Default | Description |
|---|---|---|
url | required | Control plane origin, no path. |
enrollment_token_env | empty | Environment variable holding the one-time enrollment token. The token is never read from the YAML file itself. |
credential_file | required | Where the runtime credential is stored after enrollment. |
request_timeout | 15s | Timeout for individual control-plane requests. |
heartbeat_interval | 30s | How often a content-free heartbeat is sent. |
calibration
Calibration builds the behavioural baseline from clean traffic. The agent does not alert during calibration. Once both min_samples and min_duration are met, calibration activates and scoring begins.
| Field | Default | Description |
|---|---|---|
state_file | required | Where calibration state is persisted. |
min_samples | required | Minimum clean exchanges before activation (at least 2). |
max_samples | required | Maximum samples retained in the baseline. |
min_duration | required | Minimum wall-clock time before activation. |
alert_threshold | 0.75 | Anomaly score in (0, 1] above which a session is flagged. |
reset_on_extractor_change | false | When false, an extractor upgrade marks the baseline stale until an administrator installs a new revision or resets calibration. |
Build the baseline from clean traffic
Calibrating over a period that already contains an ongoing attack teaches the model that the attack is normal. Anomaly scores will look reassuring while nothing is being caught.
sessions
| Field | Default | Description |
|---|---|---|
ttl | required | How long an idle session lives in memory. |
max_sessions | required | LRU ceiling on concurrent sessions. |
max_turns | required | Per-session turn bound. Older turns are dropped. |
spool
The encrypted disk spool buffers telemetry locally when the control plane is unreachable. The spool is bounded: when full, the oldest items are evicted so new telemetry can be written. An AES-256 key is auto-generated alongside the queue directory on first startup.
| Field | Default | Description |
|---|---|---|
directory | required | Where spool files are written. |
encryption_key_file | auto | AES-256 key path. Defaults to a sibling of the spool directory. |
max_bytes | required | Total spool size ceiling in bytes. |
max_items | required | Maximum queued items. |
batch_size | 100 | Items per upload batch (max 200). |
flush_interval | 5s | How often the uploader checks for queued items. |
min_backoff | 1s | Initial retry delay after an upload failure. |
max_backoff | 1m | Maximum retry delay. |
policy
All policy checks are observe-only. Exceeding a limit or using an unlisted capability creates a detection but never blocks or modifies model traffic.
| Field | Description |
|---|---|
allowed_tools | Allowlist of tool names the model may invoke. Use [] to deny all tools. |
tool_kinds | Maps each allowed tool name to read, write, delete, admin, or external. |
allowed_capabilities | Which tool kinds are permitted for this agent. |
dangerous_sequences | Ordered tool-kind chains that create a detection. For example, ["read", "external"] flags a read followed by an external call. |
degradation_threshold | Anomaly score at which CBE switches the session to observe-only degradation. |
policy.limits
| Field | Description |
|---|---|
max_request_bytes | Observe-only request size ceiling. |
max_messages | Maximum messages in a single request. |
max_input_tokens | Estimated input token ceiling. |
max_output_tokens | Output token ceiling. |
max_total_tokens | Combined token ceiling per turn. |
max_tool_calls | Session-wide tool call ceiling. |
max_api_calls | Session-wide external API call ceiling. |
max_file_ops | Session-wide file operation ceiling. |
lcac
The LCAC (context access control) engine evaluates assembled prompt context against a Policy-as-YAML ruleset. In the agent, a deny is a detection, never a blocked request.
| Field | Default | Description |
|---|---|---|
enabled | true | Set to false to disable the LCAC engine entirely. |
identity_headers | See below | Maps attribute names to HTTP headers. Defaults to X-CoreLayer-Tenant-ID and X-CoreLayer-Role. |
namespace_header | X-CoreLayer-Namespace | Header carrying the data namespace for context scoping. |
time_header | X-CoreLayer-Request-Time | Header carrying the local request time for time-of-day conditions. |
policy | default | Inline LCAC policy. Covers identity, context, actions, conditions, and data flow rules. |
Gateway endpoints
| Path | Method | Purpose |
|---|---|---|
/v1/chat/completions | POST | Transparent proxy. Your application sends its normal OpenAI requests here. |
/healthz | GET | Liveness. Returns the agent ID, model ID, and extractor version. |
/readyz | GET | Readiness. Returns 503 until background workers have started. |
/metrics | GET | Prometheus exposition format. All counters and gauges listed below. |
Prometheus metrics
The /metrics endpoint serves Prometheus exposition format with no external dependencies. Point your scrape config at port 8090.
| Metric | Type | Description |
|---|---|---|
corelayer_fingerprint_ready | gauge | 1 when the gateway is accepting traffic. |
corelayer_fingerprint_requests_total | counter | Chat completion requests received. |
corelayer_fingerprint_stream_requests_total | counter | Streaming requests received. |
corelayer_fingerprint_upstream_failures_total | counter | Upstream transport failures. |
corelayer_fingerprint_observations_dropped_total | counter | Observations dropped from the bounded queue. |
corelayer_fingerprint_policy_detections_total | counter | Observe-only policy detections. |
corelayer_fingerprint_anomaly_events_total | counter | LBF anomaly events. |
corelayer_fingerprint_spool_items | gauge | Items currently queued in the disk spool. |
corelayer_fingerprint_spool_bytes | gauge | Bytes currently used by the disk spool. |
corelayer_fingerprint_upload_successes_total | counter | Successful ingest batches sent to the control plane. |
corelayer_fingerprint_upload_failures_total | counter | Failed ingest attempts. |
corelayer_fingerprint_calibration_samples | gauge | Samples in the current baseline. |
corelayer_fingerprint_calibration_state | gauge | Label vector with calibrating, active, and stale states. |
Session tracking
The agent groups exchanges into sessions so the LBF and CBE engines can reason about multi-turn behaviour. Session IDs are derived per process using an ephemeral HMAC key. The source material is never stored or reported.
Resolution order:
X-CoreLayer-Session-IDheader, if present. Use this for explicit session control.- The
userfield in the OpenAI request body. - A privacy-preserving fingerprint of the connection metadata (remote address, user agent, authorization header). This is a last resort and may group unrelated requests.
Sessions are bounded by sessions.ttl, sessions.max_sessions, and sessions.max_turns. Idle sessions are evicted by TTL; when the LRU ceiling is reached, the least recently active session is removed.
Privacy and content capture
By default, prompt and response content never leaves the agent process. Telemetry sent to the control plane contains only structured metadata: token counts, tool names, anomaly scores, and detection codes.
When capture_content is true, anomaly and detection events include redacted snippets (up to 2048 characters each). The redaction pipeline strips API keys, bearer tokens, private keys, JWTs, email addresses, SSNs, phone numbers, and credit card patterns before the evidence is written to the spool. Enabling or disabling content capture creates a new immutable dashboard revision and requires a reinstall.
Credential headers are never copied
The LCAC engine reads identity and namespace from configured headers, but Authorization, Cookie, X-API-Key, and other credential headers are rejected at config validation. They cannot be mapped into LCAC attributes or telemetry.
Network requirements
| Direction | Destination | When |
|---|---|---|
| Inbound TCP 8090 | From the protected application or internal load balancer only | Always |
| Outbound HTTPS | upstream.url (your OpenAI-compatible endpoint) | Always |
| Outbound HTTPS | control_plane.url (CoreLayer control plane) | Always |
| Outbound HTTPS | get.corelayersecurity.com, ghcr.io | Installation only |
| Outbound DNS | Customer resolver | Always |
No inbound control-plane connection is required. If control-plane egress is temporarily blocked, model traffic continues and telemetry is queued under the bounded spool. A full spool drops the oldest items rather than interrupting inference.
File layout
All three installation methods (Docker, systemd, Helm) use the same paths inside the service boundary:
| Path | Purpose |
|---|---|
/etc/corelayer/agent.yaml | Configuration file (read-only after startup). |
/var/lib/corelayer/fingerprint/credential.json | Runtime credential written after enrollment (mode 0600). |
/var/lib/corelayer/fingerprint/calibration.json | Behavioural baseline state. |
/var/lib/corelayer/fingerprint/spool/ | Encrypted telemetry queue. |
/var/lib/corelayer/fingerprint/spool.key | AES-256 spool encryption key. |
Verifying a release
Every versioned directory includes a SHA256SUMS manifest that the release workflow signs keyless with its GitHub identity. The install script verifies the signature automatically when cosign is present and falls back to the checksum alone otherwise.
base=https://get.corelayersecurity.com/agent/v0.1.0
curl -fsSLO "$base/SHA256SUMS"
curl -fsSLO "$base/SHA256SUMS.sig"
curl -fsSLO "$base/SHA256SUMS.pem"
cosign verify-blob SHA256SUMS \
--signature SHA256SUMS.sig \
--certificate SHA256SUMS.pem \
--certificate-identity-regexp '^https://github.com/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum -c SHA256SUMSRemoval and data
Stopping or upgrading the agent does not delete the baseline, spool, or runtime credential. Preserve the data directory or PVC for in-place binary upgrades. To remove all local state, stop the service first, revoke the installation in the dashboard, then delete /var/lib/corelayer/fingerprint, the Compose volume, or the Kubernetes PVC. Deleting state starts a new calibration window and requires a fresh enrollment token.
Troubleshooting
The agent exits at startup
- Config permissions. The YAML file must not be group-writable or world-writable. Check with
ls -l /etc/corelayer/agent.yaml. - Missing enrollment token. On first startup, the environment variable named in
enrollment_token_envmust contain the dashboard token. Once a runtime credential has been written, the variable is no longer needed. - Validation errors. The agent validates every field strictly. A missing required field, an invalid URL, or overlapping file paths (credential, calibration, spool) will print a specific error and exit.
Calibration stays in "calibrating"
Both min_samples and min_duration must be met. If traffic is low, reduce min_samples or wait longer. Only clean exchanges count: upstream errors, policy detections, truncated responses, and incomplete streams are excluded from calibration.
No events appear in the dashboard
- Check
/readyz. If it returns 503, the background workers have not started. - Check the
corelayer_fingerprint_upload_failures_totalmetric. A non-zero value means the control plane is rejecting batches or is unreachable. - Check
corelayer_fingerprint_spool_items. Items queuing up means delivery is blocked. Check network egress to the control plane URL.
Upstream latency increased
The agent adds no computation to the response path. Observation parsing happens asynchronously after the response has been delivered. If latency increases, check the network path between the agent and the upstream rather than the agent itself. The /metrics endpoint shows upstream failure counts for diagnosis.