Docs

Settings

The console's Settings page, section by section, and how a signed change takes effect. Includes how to verify a webhook.

On this page

Every setting lives in the console, on one page, /settings. There is no file to edit and no command to run for a setting while the console works. The page has seven sections, each an anchor, with a list on the left (a select on a phone).

How a setting changes

Each row follows one pattern. You press Change, and the console drafts the change. A keeper signs the draft with a passkey, and the signature covers exactly that change. A change that makes Warden louder or more careful takes effect at once. A change that quiets something, widens access or loosens a limit can wait first, by the class of the change, and the row shows the wait, who signed it and when it ends. With one keeper the page says so: “You are the only keeper. Dangerous changes wait; a second keeper ends the waits.”

No model output can change a setting, suppress a finding or mark anything safe. Agents may propose; a person signs.

Each row also shows where its value came from: the default that came with the release you run, or the signature that set it. Rows marked Details sit behind a toggle so the common path stays short.

Install parameters

The values setup chose, such as the node group Warden runs on and its footprint, are not frozen at setup. The console’s Install › Parameters lists every keeper parameter of the release you run, with its effective signed value and a Change button. A change is a signed install.params record, and the install gate applies it. A few values seal the install at its first signature; they show “Fixed after the first signature.” and no Change.

The console is required: the first signature and every signed change happen in it.

Watching

What Warden reads, and how often.

  • Accounts, Regions and Clusters. All, only these, or all except these. The organization is detected. The hub account and the cluster Warden runs in are always watched and shown fixed. Each cluster row has a Seen column that shows what the last run actually read; narrowing skips a place and deletes nothing.
  • CloudTrail. The discovered trail, read only, with a Change button.
  • Network sensor. Off by default, with a dollar line for the flow-log volume before you turn it on.
  • Schedule. Every day at a time, or every N hours. Pause for one hour to seven days, then Resume; while a signed pause runs, the digest says who paused it and until when. Run-now limits and per-run read budgets sit behind Details, each with its dollar line.
  • What it checks. One checkbox per rule pack; core is shown fixed.
  • What matters most. The asset classes, sensitive buckets and signing APIs you want weighted higher.
  • Reading depth (Details). Optional protections and Kubernetes values to read.

A raise that more than doubles the projected monthly cost, or adds more than the release’s dollar line, waits like a widening of access, so one phished keeper cannot run up the bill.

Agents

  • On or off. Off shows “rules only” on every page.
  • Models per role. For each role (lead, triage, hunt, refuter), a list of the models your account has enabled, each with a Check button for Bedrock model access. See Bedrock access and Install checks.
  • Budget. The day, the attack reserve and the chat allowance, each with the month’s price.
  • Proposals. What agents may propose: suppressions, hardening fixes, hunt leads.
  • Standing instructions. Written guidance the agents read.

The Agents page shows activity and transcripts.

Defend

On or off and the classes it may act in, Pause and Resume, the limits (protected identities, break-glass ids, caps, time fence, block sets), what Defend is Holding now with a countdown, Undo and Undo all, and standing orders with End.

Defend prepares a plan; a keeper signs it with a passkey. With no Defend license the section is one sentence and a link to the plan; nothing is greyed out row by row.

People

  • Keepers. A keeper is a person whose passkey approves changes. Invite, confirm by comparing the fingerprint words, and remove.
  • Viewers. People who can open the console and read it.
  • Your passkeys. Add, freeze or remove, with the class of each key shown. Any passkey works by default.
  • Approvals needed. How many keepers a kind of change needs.
  • Security (Details). The recovery wait, minimum key type, second path, waiting periods and notice hold. Stricter assurance is opt-in and signed.

If the console or its door is broken, see Recovery.

Alerts

  • E-mail. Addresses for alarms, reports and notices. The owner address you gave at setup is shown fixed here: every readback and every waiting change is mailed to it. Only Install › Parameters changes it, by a root change that waits 72 hours and is mailed to the old address, and the owner address can never be emptied. A notice address cannot be the owner address, which already receives every notice. Adding a notice address or removing any channel waits 72 hours and is mailed to the owner address.
  • Slack. Paste an incoming-webhook URL, then Send test once the channel is in effect.
  • Webhooks. A URL and a signing key. Each run sends one signed wardensec.run webhook to every signed JSON channel. To check a delivery, see Verify a webhook.
  • Digest. The lowest severity it carries, time zone, the weekday of the weekly board, and the number of items.
  • Exports and Metrics (Details). Security Hub, EventBridge and files; Warden’s own metrics.

Exceptions

  • Suppressions. One finding marked not a problem, with a ceiling and an expiry. Findings at the highest severity need the stronger signature.
  • Class passes. Accept a whole class of finding for a period, with its ceiling exact. A pass quiets only the findings it names: when it matched more than it may name, the others stay loud. A new resource that matches a pass waits in Needs you; until a keeper lets this one through, it stays loud, and so do the others that pass quiets.
  • Known good. Seven lists, plus the CDN in front of you, your egress ranges and your user pools. A range wider than a /8 for IPv4 or a /24 for IPv6 is refused. What an entry would quiet is measured on the latest complete run, and you choose its ceiling on the form. When a new resource matches an entry beyond its ceiling, Needs you offers Let this one through; until a keeper lets this one through, it stays loud, and so does everything that entry quiets. If that entry came from the previous generation and cannot be widened, it and everything it quiets stay loud: end the entry and sign it again from the latest run.
  • Never silenced. A fixed list that no setting can quiet.

Each entry shows what it quiets, how many findings it covers today, its ceiling, who signed it, when it expires, and Renew and End buttons.

Licence

The plan, the features it grants, the expiry, and Add licence to paste or upload a license file. See Licensing. The watch continues in every license state.

Verify a webhook

Every webhook Warden sends is signed by the install’s courier key. The signature covers the target it was sent to, the delivery id, the time and the exact body, so a message sent to one target cannot be replayed to another target, or later. There is no shared secret: the receiver holds the courier’s public key, in PEM, and its own target id, which the install hands you together when the webhook channel is added.

The scanner’s push step is the sender. Its courier key is derived from the signing key your keepers staged in Settings under Alerts, so staging a new signing key rotates it. Each run that holds a staged key publishes the public half in the install’s state bucket, under the instance’s own prefix at wardensec/ + the instance id + /notify/courier-key.json, as {"v":1,"key_id":"<16 hex>","public_key":"<SPKI PEM>"}. Give the receiver that public_key and pin key_id: it is the k of every signature below.

Each request carries two headers:

  • wardensec-delivery: <delivery_id>/<target_id>
  • wardensec-signature: k=<key id>;t=<unix seconds>;s=<base64url DER signature>

k is the first 16 hex characters of the SHA-256 of the courier key’s SPKI. The signature is ECDSA P-256 with SHA-256 over:

"wardensec.webhook.v2" 0x00 target_id 0x00 delivery_id 0x00 t 0x00 body

A receiver must:

  1. read the raw body bytes before any JSON parsing;
  2. check that k names the courier key it holds;
  3. reject a time more than 300 seconds from its own clock, either way;
  4. check the signature over the exact bytes above;
  5. reject a delivery id it has already accepted (keep the ids for at least ten minutes);
  6. reject a target id other than its own, so a delivery signed for another target is never taken as its own.

The check in Node.js, with nothing but node:crypto:

const { createHash, createPublicKey, verify } = require("node:crypto");

// recordFirst(deliveryId) stores the id and returns, or resolves to, true only the first time, as one atomic
// step: an insert on a unique key, or SET NX. A separate check then write lets two copies of one delivery both pass.
// It returns a Promise, which is always truthy: await it and compare with true, never `if (!verifyWardenWebhookAsync(...))`.
//   if ((await verifyWardenWebhookAsync(headers, rawBody, publicKeyPem, ownTargetId, recordFirst)) !== true) return reject();
// A courier retry of a delivery whose id is already recorded is refused as a replay, so a receiver that fails after
// recordFirst loses it: record the id in the same transaction as the processing, or answer 2xx once it is recorded
// and process from your own queue.
async function verifyWardenWebhookAsync(headers, rawBody, publicKeyPem, ownTargetId, recordFirst, nowS = Date.now() / 1000) {
  const sig = /^k=([0-9a-f]{16});t=([0-9]{1,12});s=([A-Za-z0-9_-]+)$/.exec(headers["wardensec-signature"] || "");
  const del = /^([A-Za-z0-9_-]{1,40})\/([A-Za-z0-9_-]{1,40})$/.exec(headers["wardensec-delivery"] || "");
  if (!sig || !del) return false;
  const [, k, t, s] = sig;
  const [, deliveryId, targetId] = del;
  const key = createPublicKey(publicKeyPem);
  const kid = createHash("sha256").update(key.export({ type: "spki", format: "der" })).digest("hex").slice(0, 16);
  if (k !== kid) return false;
  if (Math.abs(nowS - Number(t)) > 300) return false;
  if (targetId !== ownTargetId) return false;
  const message = Buffer.concat([
    Buffer.from(`wardensec.webhook.v2\0${targetId}\0${deliveryId}\0${t}\0`, "utf8"),
    Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody, "utf8"),
  ]);
  if (!verify("sha256", message, key, Buffer.from(s, "base64url"))) return false;
  return (await recordFirst(deliveryId)) === true;
}

recordFirst(deliveryId) stores the id and returns, or resolves to, true only the first time, as one atomic step: an insert on a unique key, or SET with NX. Checking and then recording in two steps lets two copies of one delivery both pass.

The function is async, so it returns a Promise, and a Promise is always truthy: if (!verifyWardenWebhookAsync(...)) would accept a forgery. Await it and compare with true:

if ((await verifyWardenWebhookAsync(headers, rawBody, publicKeyPem, ownTargetId, recordFirst)) !== true) return reject();

A retry of a delivery whose id was already recorded is refused as a replay. So a receiver records the id in the same transaction as its processing, or answers 2xx once the id is recorded and processes from its own queue: otherwise a delivery that failed after recordFirst is never processed.

What a target receives

A JSON target receives the whole body: a text line for people and a wardensec object with the message type, the run or the alarm, the license state and the Witness summary. A chat target receives {"text": "..."} only, which every chat tool’s incoming webhook shows.

Chat targets are unauthenticated by nature: anyone who holds the URL can post into that channel, and the chat tool shows no signature. Treat a chat message as a doorbell and read the console for the facts.

A body the outbound gate refuses is never sent. The target receives a fixed sentence in its place, “Run ‹id› finished; the report was withheld by the gate: ‹code›.”, signed the same way.

How Warden sends

  • HTTPS only, to port 443, to a DNS name. The address the name resolves to must be public; Warden resolves it once and connects to that address, so a second answer cannot redirect the request.
  • No redirect is followed, and the response body is never read: only the status counts.
  • Three attempts, at 0, 2 and 8 seconds, on a network error or a 5xx. A 4xx or a redirect is final.