Katar Native Agent
A production-grade, open-protocol security telemetry agent. Captures kernel events via eBPF, evaluates behavioral rules in sandboxed WebAssembly, streams signed telemetry over mTLS gRPC, and enforces network blocks at wire-speed via XDP.
GraphQL API
Query live agent state, stream telemetry, dispatch signed enforcement actions.
Agent Enrollment
Cert pinning, TPM identity, and Control Plane key provisioning.
Operations
Startup sequence, env vars, build commands, and troubleshooting.
NOS Protocol
gRPC contract, signing spec, and multi-language codegen guide.
Architecture
KNA follows the industry-standard pattern of kernel-adjacent work in Rust, control/integration layer in Go — the same split used by Falco, Cilium, and Tetragon.
GraphQL API
http://localhost:8080/queryhttp://localhost:8080/playgroundAuthentication
All Mutation operations require a Bearer token:
Authorization: Bearer <api_key>
Configured in katar-control-plane/config.yaml under server.api_key. Query operations are unauthenticated.
Types
| Field | Type | Description |
|---|---|---|
| id | ID! | TPM-derived UUID |
| hostname | String! | From config.yaml |
| addr | String! | gRPC address |
| lastSeen | String! | RFC 3339 timestamp |
| version | String! | Agent version |
| status | String! | ONLINE / OFFLINE / CONNECTING |
| telemetryCount | Int! | Events received since CP start |
| Field | Type | Description |
|---|---|---|
| agentId | String! | Source agent UUID |
| eventId | String! | Unique event ID |
| timestamp | Int! | Unix seconds |
| eventType | String! | process / network / file / gap |
| Field | Type | Description |
|---|---|---|
| success | Boolean! | Agent accepted the action |
| message | String! | Result or error detail |
Queries
List all fleet agents, regardless of current connection status.
query {
agents {
id
hostname
addr
status
lastSeen
version
telemetryCount
}
}
{
"data": {
"agents": [{
"id": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
"hostname": "app-server-01",
"addr": "localhost:50051",
"status": "ONLINE",
"lastSeen": "2026-04-27T22:15:03Z",
"version": "v2.0.0",
"telemetryCount": 4821
}]
}
}
Returns up to limit events from an agent's ring buffer. Events persist even while the agent is OFFLINE.
query RecentEvents($agentId: String!, $limit: Int!) {
recentEvents(agentId: $agentId, limit: $limit) {
agentId
eventId
timestamp
eventType
}
}
{ "agentId": "6ba7b811-...", "limit": 20 }| eventType | Source |
|---|---|
process | eBPF tracepoint — exec/exit |
network | eBPF XDP/TC — connection event |
file | eBPF LSM — open/write/unlink |
gap | Offline queue drain — events dropped |
query {
destinations {
id
name
type
url
enabled
}
}
Mutations
Authorization: Bearer <api_key>Dispatches a cryptographically signed enforcement action to the target agent. The Control Plane signs with its Ed25519 private key; the agent verifies before applying.
mutation {
executeAction(
agentId: "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
action: "ENFORCE_XDP_BLOCK"
targetId: "1.2.3.4"
) {
success
message
}
}
| action | targetId format | Example |
|---|---|---|
ENFORCE_XDP_BLOCK | IPv4 address | "1.2.3.4" |
UNBLOCK_XDP_IP | IPv4 address | "1.2.3.4" |
KILL_PROCESS | PID (decimal string) | "3821" |
QUARANTINE_FILE | Absolute path | "/tmp/malware.sh" |
PATCH_PACKAGE | Package name | "openssl" |
curl -X POST http://localhost:8080/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer change-me-before-production" \
-d '{"query":"mutation { executeAction(agentId:\"<UUID>\", action:\"ENFORCE_XDP_BLOCK\", targetId:\"1.2.3.4\") { success message } }"}' | jq
mutation {
createDestination(input: {
name: "Splunk HEC"
type: "splunk"
url: "https://splunk.example.com:8088"
}) { id name enabled }
}
mutation {
toggleDestination(id: "dest-0", enabled: true) {
id name enabled
}
}
Error Reference
| Error | Cause | Fix |
|---|---|---|
unauthorized: Bearer token required | Missing/wrong Authorization header | Set Authorization: Bearer <api_key> |
agent <id> not found | ID not in registry | Check agents query — ID must match exactly |
agent <id> is OFFLINE | Agent disconnected | Wait for reconnect or check agent host |
unknown action: <name> | Invalid action string | Use exact strings from action types table |
Action timestamp is invalid or expired | Clock skew > 5s | Sync NTP on both Control Plane and agent hosts |
Invalid cryptographic signature. Access Denied. | Wrong KATAR_ACTION_PUBKEY on agent | Set env var to hex key printed by Control Plane on boot |
Full Schema (SDL)
type Agent {
id: ID!
hostname: String!
addr: String!
lastSeen: String!
version: String!
status: String!
telemetryCount: Int!
}
type TelemetryEvent {
agentId: String!
eventId: String!
timestamp: Int!
eventType: String!
}
type DestinationConfig {
id: ID!
name: String!
type: String!
url: String!
enabled: Boolean!
}
type ActionResult {
success: Boolean!
message: String!
}
input NewDestination {
name: String!
type: String!
url: String!
headers: String
}
type Query {
agents: [Agent!]!
destinations: [DestinationConfig!]!
recentEvents(agentId: String!, limit: Int!): [TelemetryEvent!]!
}
type Mutation {
createDestination(input: NewDestination!): DestinationConfig!
toggleDestination(id: ID!, enabled: Boolean!): DestinationConfig!
executeAction(agentId: ID!, action: String!, targetId: String!): ActionResult!
}
Agent Enrollment
Connect a katar-node agent to the katar-control-plane.
Start the Control Plane
cd katar-control-plane
go run ./cmd/katar-manager/
On first boot, a new Ed25519 keypair is generated and the public key is printed to stdout. Save this hex string.
Start the Agent
KATAR_ACTION_PUBKEY=<64-char-hex> cargo run --release
The agent logs its UUID and TLS cert fingerprint:
INFO Agent Identity established. UUID: 6ba7b811-...
INFO Agent TLS cert fingerprint (SHA-256): a3f9c2...
INFO Starting secure gRPC API Server on 0.0.0.0:50051
Copy the Agent Certificate
Same host (dev): reference the cert directly in config.yaml.
Remote host:
scp user@agent-host:/path/to/katar-node/certs/agent.crt \
/etc/KATAR/agents/agent-001.crt
# Verify fingerprint matches Step 2
openssl x509 -in agent-001.crt -fingerprint -sha256 -noout
Add Agent to config.yaml
agents:
- id: "6ba7b811-9dad-11d1-80b4-00c04fd430c8" # UUID from agent log
hostname: "app-server-01"
addr: "app-server-01:50051"
tls_cert: "/etc/KATAR/agents/agent-001.crt"
version: "v2.0.0"
Verify in Dashboard
Open http://localhost:8080 — the agent should appear with Status: ONLINE and telemetryCount incrementing.
Operations Runbook
Startup Sequence
1. Start katar-control-plane → save printed Ed25519 public key
2. Start katar-node with KATAR_ACTION_PUBKEY=<hex>
3. Copy certs/agent.crt to Control Plane host (if remote)
4. Add agent to config.yaml, restart Control Plane
Environment Variables
| Variable | Service | Description |
|---|---|---|
KATAR_ACTION_PUBKEY | katar-node | 64-char hex Ed25519 public key from Control Plane. Required for executeAction to work. |
PORT | katar-control-plane | HTTP port (default: 8080) |
Build Commands
# Agent (Rust)
cargo build --release -p katar-node
cargo test -p katar-node
# Control Plane (Go)
go build -o katar-manager ./cmd/katar-manager/
go run github.com/99designs/gqlgen generate # after schema changes
make proto # after proto changes
# CI drift check
./scripts/check_proto_drift.sh
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Agent shows OFFLINE immediately | TLS cert path wrong or agent not reachable | Check tls_cert in config.yaml; check agent is running on correct port |
| executeAction returns Access Denied | Wrong KATAR_ACTION_PUBKEY | Use hex key printed by Control Plane on first boot |
| Action timestamp expired | Clock skew > 5s | Run ntpdate pool.ntp.org on both hosts |
| go build fails — undefined types | Schema updated without regenerating | Run go run github.com/99designs/gqlgen generate |
| TLS handshake fails — cert expired | Old cert from before RC2 fix | Delete certs/agent.crt and agent.key, restart agent |
NOS Protocol v2.0
The proto files in kos/proto/ are the single source of truth for all KOS v2 definitions. Neither katar-node nor katar-control-plane owns the contract.
Services
service StreamService {
// Bidirectional stream — agents push telemetry, plugins push WASM rules
rpc Subscribe (stream StreamRequest) returns (stream KOSEvent);
}
service ActionService {
// Signed enforcement actions — see signing contract below
rpc ExecuteAction (ActionRequest) returns (ActionResponse);
}
Action Signing Contract
"{action_enum_int32}:{target_id}:{unix_timestamp_seconds}"
Example (ENFORCE_XDP_BLOCK = 4):
"4:192.168.1.50:1714230000"
- Signed with Ed25519 using the Control Plane's private key
- Encoded as
base64.StdEncoding - Replay window: ±5 seconds — ensure NTP sync
- Go signer:
internal/auth/identity.go → SignActionRequest() - Rust verifier:
src/core/action_engine.rs → verify_action()
Codegen
| Language | Command |
|---|---|
| Go | cd katar-control-plane && make proto |
| Rust | Automatic — cargo build triggers build.rs |