Sealrdocs
Reference

Reason codes

Every reason code a Sealr guard can emit, what triggers it, and the guard's default decision.

A reason code is the guard's answer to "why". It appears in the record's verdict, in the Console, and in the deny message your agent sees. Policy rules match on these codes, so they are a stable API: the registry is append-only, and removing or renaming a code is a breaking change.

How to read the tables

Default decision is what the guard concludes when no policy rule matches. It is a conservative fallback, not a setting. A policy rule that matches the operation overrides it in either direction — your policy can downgrade SQL_DESTRUCTIVE_DDL to a warning, or block on TF_DELETE_PRESENT.

Risk class (Low, Medium, High, Critical) travels with the record and is what dashboards and the Exposure Report aggregate on.

Decisions are ordered ALLOW < WARN < REQUIRE_APPROVAL < BLOCK. When several codes fire on one operation, the strongest decision and the highest risk class win, and every code is recorded.

Core / pipeline

These come from the evaluation pipeline itself, before or around a guard.

CodeTriggerDefault decisionRisk
INPUT_TRUNCATEDOperation payload exceeded the 1 MiB cap. The guard never ran — oversized input is not parsed.REQUIRE_APPROVALHigh
GUARD_PANICThe guard panicked. Caught at the pipeline boundary; the record is written as guard_error.REQUIRE_APPROVALHigh
GUARD_TIMEOUTRegistered for a guard that exceeds its 25 ms budget.——
POLICY_STALERegistered for decisions made against a policy cache older than 24 h.——
UNKNOWN_SURFACERegistered for an operation arriving on an unrecognized surface or method.——

GUARD_TIMEOUT, POLICY_STALE and UNKNOWN_SURFACE are registered and selectable in policy rules, but no code path emits them today. Guard evaluation runs synchronously without a wall-clock timeout; staleness is handled through the binding's stale_action rather than by attaching a reason code; unrecognized surfaces route to the generic guard, which emits no codes at all. A rule that matches only on one of these three will never fire. Check sealr status for policy_stale instead.

The generic guard registers no codes of its own. An operation that no guard claims is recorded with an empty reason list — recorded, but not classified.

SQL guard

The SQL guard parses with the real PostgreSQL grammar (libpg_query). It is pure: no network, no clock, no connection to your database.

CodeTriggerDefault decisionRisk
SQL_UNBOUNDED_WRITEUPDATE or DELETE with no WHERE clause.BLOCKCritical
SQL_TAUTOLOGY_WHEREWHERE reduces to a constant-true predicate — WHERE 1=1, WHERE true.BLOCKCritical
SQL_DANGEROUS_FUNCTIONCOPY ... TO PROGRAM or COPY with a server-side filename, a DO $$ block, ALTER SYSTEM, or a call to a file/session function (see below).BLOCKCritical
SQL_DESTRUCTIVE_DDLDROP of a table, schema, database, materialized view, sequence or foreign table; TRUNCATE; DROP DATABASE; ALTER TABLE ... DROP COLUMN or DROP CONSTRAINT.REQUIRE_APPROVALCritical
SQL_PRIVILEGE_CHANGEGRANT, REVOKE, CREATE/ALTER/DROP ROLE, ALTER DEFAULT PRIVILEGES.REQUIRE_APPROVALHigh
SQL_PARSE_ERRORThe input did not parse as PostgreSQL. Malformed SQL is never silently allowed on a write surface.REQUIRE_APPROVALHigh
SQL_DIALECT_UNSUPPORTEDEmitted alongside SQL_PARSE_ERROR when the text carries non-PostgreSQL markers — a backtick, or SQL_NO_CACHE.REQUIRE_APPROVALHigh
SQL_CTE_WRITEA data-modifying CTE: WITH ... INSERT/UPDATE/DELETE.REQUIRE_APPROVAL on a SELECT; on a write statement it raises the risk to at least High and leaves the decision to that statement.High
SQL_STRUCTURAL_DDLNon-destructive DDL: CREATE TABLE, index, view, CREATE TABLE AS, schema, sequence, extension, COMMENT, RENAME, a non-destructive ALTER TABLE, SELECT INTO, or a DROP of an object type outside the destructive list.WARNMedium
SQL_MULTI_STATEMENTMore than one statement in one operation. Carries no decision of its own — the verdict is the maximum severity across the statements.——
SQL_RADIUS_UNAVAILABLERegistered for a degraded Connected-Mode EXPLAIN radius.——

CREATE FUNCTION is the one exception to the SQL_STRUCTURAL_DDL row: it emits that code but defaults to REQUIRE_APPROVAL at High risk, because a function body is code that runs later.

SQL_RADIUS_UNAVAILABLE is registered but never emitted. Connected-Mode EXPLAIN radius is not implemented — the SQL guard always uses static estimation, and radius.method is static or none on every record.

Functions treated as session-dangerous: pg_read_file, pg_read_binary_file, pg_ls_dir, pg_stat_file, lo_import, lo_export, pg_reload_conf, pg_rotate_logfile, dblink, dblink_exec, dblink_connect, pg_terminate_backend. The scan walks every function call in the parse tree, including nested ones.

Statements outside all of these classes — EXECUTE of a prepared statement, MERGE, anything unrecognized — default to REQUIRE_APPROVAL at High risk with no reason code. Conservative by construction: an unclassified statement is not an allowed one.

Terraform guard

The Terraform guard analyzes terraform show -json plan output. Raw HCL is recorded, not analyzed.

CodeTriggerDefault decisionRisk
TF_PROTECTED_RESOURCE_DELETEThe plan deletes or replaces a resource whose type matches a protected pattern.REQUIRE_APPROVALCritical
TF_DELETE_THRESHOLD_EXCEEDEDMore than 3 deletions in one plan.REQUIRE_APPROVALHigh
TF_STATE_COMMANDA state-manipulation subcommand in the argv: state rm, state mv, import, taint, untaint, force-unlock, apply -replace. These are invisible in a plan.REQUIRE_APPROVALHigh
TF_PLAN_PARSE_ERRORThe payload looked like JSON but did not parse as a plan.REQUIRE_APPROVALHigh
TF_DELETE_PRESENTThe plan contains at least one resource deletion.WARNMedium
TF_REPLACE_PRESENTThe plan contains at least one delete+create replacement, in either order.WARNMedium
TF_HCL_UNANALYZEDThe payload on a Terraform surface is not JSON, so it is raw HCL. Recorded, not analyzed.WARNMedium

Protected type patterns compiled in for V1 (glob-lite, * and ?):

aws_db_instance          aws_rds_cluster           aws_s3_bucket
aws_dynamodb_table       aws_kms_key               google_sql_database_instance
google_storage_bucket    azurerm_*sql*             azurerm_storage_account
*_iam_*                  kubernetes_namespace

The deletion threshold is 3 and the patterns above are compiled in. Both are described in the spec as policy-extensible; in V1 they are not configurable from a policy file.

Git guard

The Git guard reads three input shapes: wrapper argv (git push ...), hook context, and GitHub App events. Content-based detection needs diff visibility, which the App does not have.

CodeTriggerDefault decisionRisk
GIT_SECRET_PATTERNA curated secret pattern matched in visible diff content.BLOCKCritical
GIT_FORCE_PUSH_PROTECTEDForce push targeting a protected branch.BLOCKCritical
GIT_REF_DELETERemote branch or tag deletion: --delete, -d, or a :refspec.REQUIRE_APPROVALHigh
GIT_PROTECTED_DIRECT_PUSHA non-force, non-delete push straight to a protected branch.REQUIRE_APPROVALHigh
GIT_FORCE_PUSHForce push to a ref that is not protected.WARNMedium
GIT_UNSIGNED_COMMITAn unsigned commit pushed to a protected branch, when diff content is visible. The guard emits the code; the policy decides whether it matters.WARNrisk unchanged

Force is recognized from --force, -f, --force-with-lease, --force-with-lease=<ref>, and a +refspec.

Default protected branch patterns: main, master, release/*, prod*.

Secret patterns

GIT_SECRET_PATTERN is tuned for precision, not recall. This is not a DLP product: it catches credentials with recognizable shapes and will miss ones without. 25 patterns cover AWS keys, GitHub and GitLab tokens, Slack tokens and webhooks, Stripe live keys, Google API keys and service account JSON, PEM private keys, JWTs, npm, OpenAI and Anthropic keys, Twilio, SendGrid, Azure storage keys, database URLs with inline credentials, and basic-auth URLs.

On top of that, an entropy gate flags an assignment whose right-hand side is at least 24 characters, has Shannon entropy above 4.0 bits per character, and more than 50% distinct bytes — reported as high_entropy_assignment. Only the kind is recorded. The matched value never leaves the machine and never enters a record.

Using codes in policy

Reason codes are the matching surface for rules. The Console's policy builder lists the same registry this page documents, and sealr policy test shows exactly which codes an operation produces:

sealr policy test ./policy.yaml \
  --integration sql \
  --resource db:prod-main \
  --input "UPDATE accounts SET balance = 0" \
  --json
{
  "guard_decision": "Block",
  "guard_reasons": ["SQL_UNBOUNDED_WRITE"],
  "risk": "Critical",
  "final_decision": "Block",
  "matched_rule": "...",
  "mode": "Enforce"
}

Next

On this page