#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ActionCategory {
Dml,
Ddl,
Schema,
Function,
Mgmt,
Policy,
Admin,
Config,
Vault,
Wildcard,
Ai,
Notification,
Stream,
Queue,
Graph,
Ops,
Vector,
Other,
}
impl ActionCategory {
pub fn as_str(&self) -> &'static str {
match self {
ActionCategory::Dml => "dml",
ActionCategory::Ddl => "ddl",
ActionCategory::Schema => "schema",
ActionCategory::Function => "function",
ActionCategory::Mgmt => "mgmt",
ActionCategory::Policy => "policy",
ActionCategory::Admin => "admin",
ActionCategory::Config => "config",
ActionCategory::Vault => "vault",
ActionCategory::Wildcard => "wildcard",
ActionCategory::Ai => "ai",
ActionCategory::Notification => "notification",
ActionCategory::Stream => "stream",
ActionCategory::Queue => "queue",
ActionCategory::Graph => "graph",
ActionCategory::Ops => "ops",
ActionCategory::Vector => "vector",
ActionCategory::Other => "other",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LifecycleState {
Active,
Deprecated {
replacement: Option<&'static str>,
since_version: &'static str,
},
Removed,
}
#[derive(Debug, Clone)]
pub struct ActionEntry {
pub name: &'static str,
pub category: ActionCategory,
pub lifecycle_state: LifecycleState,
pub gates_description: &'static str,
}
pub const ACTIONS: &[ActionEntry] = &[
ActionEntry {
name: "select",
category: ActionCategory::Dml,
lifecycle_state: LifecycleState::Active,
gates_description: "read rows from a collection",
},
ActionEntry {
name: "write",
category: ActionCategory::Dml,
lifecycle_state: LifecycleState::Active,
gates_description: "any mutating DML (insert/update/delete)",
},
ActionEntry {
name: "insert",
category: ActionCategory::Dml,
lifecycle_state: LifecycleState::Active,
gates_description: "insert rows into a collection",
},
ActionEntry {
name: "update",
category: ActionCategory::Dml,
lifecycle_state: LifecycleState::Active,
gates_description: "update rows in a collection",
},
ActionEntry {
name: "delete",
category: ActionCategory::Dml,
lifecycle_state: LifecycleState::Active,
gates_description: "delete rows from a collection",
},
ActionEntry {
name: "truncate",
category: ActionCategory::Dml,
lifecycle_state: LifecycleState::Active,
gates_description: "truncate a collection",
},
ActionEntry {
name: "references",
category: ActionCategory::Schema,
lifecycle_state: LifecycleState::Active,
gates_description: "declare a foreign key referencing a table",
},
ActionEntry {
name: "execute",
category: ActionCategory::Function,
lifecycle_state: LifecycleState::Active,
gates_description: "execute a stored function",
},
ActionEntry {
name: "usage",
category: ActionCategory::Schema,
lifecycle_state: LifecycleState::Active,
gates_description: "use a schema namespace",
},
ActionEntry {
name: "grant",
category: ActionCategory::Mgmt,
lifecycle_state: LifecycleState::Active,
gates_description: "grant privileges to another principal",
},
ActionEntry {
name: "revoke",
category: ActionCategory::Mgmt,
lifecycle_state: LifecycleState::Active,
gates_description: "revoke privileges from another principal",
},
ActionEntry {
name: "create",
category: ActionCategory::Ddl,
lifecycle_state: LifecycleState::Active,
gates_description: "create a database object",
},
ActionEntry {
name: "drop",
category: ActionCategory::Ddl,
lifecycle_state: LifecycleState::Active,
gates_description: "drop a database object",
},
ActionEntry {
name: "alter",
category: ActionCategory::Ddl,
lifecycle_state: LifecycleState::Active,
gates_description: "alter a database object",
},
ActionEntry {
name: "schema:write",
category: ActionCategory::Schema,
lifecycle_state: LifecycleState::Active,
gates_description: "grouped DDL on the current schema namespace (foreign table, migration)",
},
ActionEntry {
name: "schema:admin",
category: ActionCategory::Admin,
lifecycle_state: LifecycleState::Active,
gates_description: "namespace-level DDL (CREATE SCHEMA, CREATE SERVER)",
},
ActionEntry {
name: "policy:put",
category: ActionCategory::Policy,
lifecycle_state: LifecycleState::Active,
gates_description: "create or update a managed policy document",
},
ActionEntry {
name: "policy:drop",
category: ActionCategory::Policy,
lifecycle_state: LifecycleState::Active,
gates_description: "delete a managed policy document",
},
ActionEntry {
name: "policy:attach",
category: ActionCategory::Policy,
lifecycle_state: LifecycleState::Active,
gates_description: "attach a policy to a principal",
},
ActionEntry {
name: "policy:detach",
category: ActionCategory::Policy,
lifecycle_state: LifecycleState::Active,
gates_description: "detach a policy from a principal",
},
ActionEntry {
name: "policy:simulate",
category: ActionCategory::Policy,
lifecycle_state: LifecycleState::Active,
gates_description: "run the policy simulator",
},
ActionEntry {
name: "kv:invalidate",
category: ActionCategory::Other,
lifecycle_state: LifecycleState::Active,
gates_description: "invalidate cached KV entries",
},
ActionEntry {
name: "admin:bootstrap",
category: ActionCategory::Admin,
lifecycle_state: LifecycleState::Active,
gates_description: "execute the bootstrap workflow",
},
ActionEntry {
name: "admin:audit-read",
category: ActionCategory::Admin,
lifecycle_state: LifecycleState::Active,
gates_description: "read the platform audit log",
},
ActionEntry {
name: "admin:reload",
category: ActionCategory::Admin,
lifecycle_state: LifecycleState::Active,
gates_description: "reload runtime configuration",
},
ActionEntry {
name: "admin:lease-promote",
category: ActionCategory::Admin,
lifecycle_state: LifecycleState::Active,
gates_description: "promote a standby instance via lease handoff",
},
ActionEntry {
name: "config:read",
category: ActionCategory::Config,
lifecycle_state: LifecycleState::Active,
gates_description: "read runtime configuration values",
},
ActionEntry {
name: "config:write",
category: ActionCategory::Config,
lifecycle_state: LifecycleState::Active,
gates_description: "mutate runtime configuration values",
},
ActionEntry {
name: "config:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any runtime configuration verb",
},
ActionEntry {
name: "vault:read_metadata",
category: ActionCategory::Vault,
lifecycle_state: LifecycleState::Active,
gates_description: "read vault entry metadata (no plaintext)",
},
ActionEntry {
name: "vault:read",
category: ActionCategory::Vault,
lifecycle_state: LifecycleState::Active,
gates_description: "reveal vault entry plaintext",
},
ActionEntry {
name: "vault:write",
category: ActionCategory::Vault,
lifecycle_state: LifecycleState::Active,
gates_description: "write or rotate vault entries",
},
ActionEntry {
name: "vault:unseal",
category: ActionCategory::Vault,
lifecycle_state: LifecycleState::Active,
gates_description: "unseal the vault master key for this session",
},
ActionEntry {
name: "vault:unseal_history",
category: ActionCategory::Vault,
lifecycle_state: LifecycleState::Deprecated {
replacement: Some("vault:read_metadata"),
since_version: "0.5.0",
},
gates_description: "read the vault unseal-event audit trail",
},
ActionEntry {
name: "vault:purge",
category: ActionCategory::Vault,
lifecycle_state: LifecycleState::Active,
gates_description: "purge (destructively remove) vault entries",
},
ActionEntry {
name: "evidence:export",
category: ActionCategory::Other,
lifecycle_state: LifecycleState::Active,
gates_description: "export evidence bundles",
},
ActionEntry {
name: "evidence:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any evidence-pipeline verb",
},
ActionEntry {
name: "red.registry:register",
category: ActionCategory::Other,
lifecycle_state: LifecycleState::Active,
gates_description: "register a new managed-config schema",
},
ActionEntry {
name: "red.registry:supersede",
category: ActionCategory::Other,
lifecycle_state: LifecycleState::Active,
gates_description: "supersede an existing managed-config schema",
},
ActionEntry {
name: "red.registry:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any registry verb",
},
ActionEntry {
name: "ai:provider:openai",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the OpenAI provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:anthropic",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the Anthropic provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:groq",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the Groq provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:openrouter",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the OpenRouter provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:together",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the Together provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:venice",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the Venice provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:ollama",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the Ollama provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:deepseek",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the DeepSeek provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:huggingface",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the HuggingFace provider for ASK / AUTO EMBED / SEARCH SIMILAR",
},
ActionEntry {
name: "ai:provider:local",
category: ActionCategory::Ai,
lifecycle_state: LifecycleState::Active,
gates_description: "use the local (in-process) embedding provider",
},
ActionEntry {
name: "ai:provider:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "use any AI provider (provider-gate wildcard)",
},
ActionEntry {
name: "ai:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any AI-namespace verb",
},
ActionEntry {
name: "notify",
category: ActionCategory::Notification,
lifecycle_state: LifecycleState::Active,
gates_description:
"publish to / subscribe to ephemeral notification channels in the principal's own tenant",
},
ActionEntry {
name: "notify:cross-tenant",
category: ActionCategory::Notification,
lifecycle_state: LifecycleState::Active,
gates_description:
"address ephemeral notification channels in another tenant or the global namespace",
},
ActionEntry {
name: "notify:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any ephemeral notification verb",
},
ActionEntry {
name: "stream",
category: ActionCategory::Stream,
lifecycle_state: LifecycleState::Active,
gates_description:
"append, read, and offset-save on durable streams in the principal's own tenant",
},
ActionEntry {
name: "stream:cross-tenant",
category: ActionCategory::Stream,
lifecycle_state: LifecycleState::Active,
gates_description:
"address durable streams in another tenant or the global namespace",
},
ActionEntry {
name: "stream:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any durable stream verb",
},
ActionEntry {
name: "queue:enqueue",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "push / produce a message onto a queue",
},
ActionEntry {
name: "queue:read",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "destructive read: pop, group-read, claim",
},
ActionEntry {
name: "queue:peek",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "non-destructive read: peek, len, pending, select",
},
ActionEntry {
name: "queue:ack",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "acknowledge a delivered queue message",
},
ActionEntry {
name: "queue:nack",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "negative-acknowledge / requeue a delivered queue message",
},
ActionEntry {
name: "queue:retry",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "override retry policy (e.g. per-failure NACK delay)",
},
ActionEntry {
name: "queue:dlq:move",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "move / replay messages between a queue and its DLQ",
},
ActionEntry {
name: "queue:purge",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "destructively purge all messages from a queue",
},
ActionEntry {
name: "queue:presence:read",
category: ActionCategory::Queue,
lifecycle_state: LifecycleState::Active,
gates_description: "read consumer presence / heartbeat snapshots",
},
ActionEntry {
name: "queue:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any queue verb",
},
ActionEntry {
name: "graph:read",
category: ActionCategory::Graph,
lifecycle_state: LifecycleState::Active,
gates_description: "read graph node/edge metadata and graph-wide properties",
},
ActionEntry {
name: "graph:traverse",
category: ActionCategory::Graph,
lifecycle_state: LifecycleState::Active,
gates_description: "execute pattern match / neighborhood / path traversal queries",
},
ActionEntry {
name: "graph:algorithm:run",
category: ActionCategory::Graph,
lifecycle_state: LifecycleState::Active,
gates_description: "run a graph analytics algorithm (centrality, community, components, ...)",
},
ActionEntry {
name: "graph:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any graph verb",
},
ActionEntry {
name: "ops:read:self",
category: ActionCategory::Ops,
lifecycle_state: LifecycleState::Active,
gates_description: "read single-instance health / lifecycle state",
},
ActionEntry {
name: "ops:read:tenant",
category: ActionCategory::Ops,
lifecycle_state: LifecycleState::Active,
gates_description: "read tenant-scoped operational metrics / aggregates",
},
ActionEntry {
name: "ops:read:cluster",
category: ActionCategory::Ops,
lifecycle_state: LifecycleState::Active,
gates_description:
"read cluster topology / replication / backup / full metrics exposition",
},
ActionEntry {
name: "ops:admin",
category: ActionCategory::Ops,
lifecycle_state: LifecycleState::Active,
gates_description:
"read security-sensitive operational state (audit log, vault posture)",
},
ActionEntry {
name: "ops:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any operational read verb",
},
ActionEntry {
name: "cluster:replication:stream",
category: ActionCategory::Other,
lifecycle_state: LifecycleState::Active,
gates_description: "stream primary WAL records and replication snapshots to a replica",
},
ActionEntry {
name: "cluster:replication:ack",
category: ActionCategory::Other,
lifecycle_state: LifecycleState::Active,
gates_description: "acknowledge replica LSN progress to the primary",
},
ActionEntry {
name: "cluster:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any cluster-scoped capability",
},
ActionEntry {
name: "vector:read",
category: ActionCategory::Vector,
lifecycle_state: LifecycleState::Active,
gates_description: "read vector metadata / data (non-search reads on a vector collection)",
},
ActionEntry {
name: "vector:search",
category: ActionCategory::Vector,
lifecycle_state: LifecycleState::Active,
gates_description: "similarity / text / hybrid search against a vector collection",
},
ActionEntry {
name: "vector:artifact:read",
category: ActionCategory::Vector,
lifecycle_state: LifecycleState::Active,
gates_description: "introspect operational vector index artifacts (pages, status)",
},
ActionEntry {
name: "vector:artifact:rebuild",
category: ActionCategory::Vector,
lifecycle_state: LifecycleState::Active,
gates_description: "rebuild / warmup vector index artifacts",
},
ActionEntry {
name: "vector:admin",
category: ActionCategory::Vector,
lifecycle_state: LifecycleState::Active,
gates_description: "admin operations on a vector collection (clustering, maintenance)",
},
ActionEntry {
name: "vector:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any vector verb",
},
ActionEntry {
name: "*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any action (escape hatch — audit usage carefully)",
},
ActionEntry {
name: "admin:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any admin verb",
},
ActionEntry {
name: "vault:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any vault verb",
},
ActionEntry {
name: "kv:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any KV verb",
},
ActionEntry {
name: "policy:*",
category: ActionCategory::Wildcard,
lifecycle_state: LifecycleState::Active,
gates_description: "any policy lifecycle verb",
},
];
pub fn is_valid_action(name: &str) -> bool {
ACTIONS
.iter()
.any(|e| e.name == name && !matches!(e.lifecycle_state, LifecycleState::Removed))
}
pub fn lookup(name: &str) -> Option<&'static ActionEntry> {
ACTIONS.iter().find(|e| e.name == name)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
const HISTORICAL_ALLOWLIST: &[&str] = &[
"select",
"write",
"insert",
"update",
"delete",
"truncate",
"references",
"execute",
"usage",
"grant",
"revoke",
"create",
"drop",
"alter",
"policy:put",
"policy:drop",
"policy:attach",
"policy:detach",
"policy:simulate",
"kv:invalidate",
"admin:bootstrap",
"admin:audit-read",
"admin:reload",
"admin:lease-promote",
"config:read",
"config:write",
"config:*",
"vault:read_metadata",
"vault:read",
"vault:write",
"vault:unseal",
"vault:unseal_history",
"vault:purge",
"evidence:export",
"evidence:*",
"red.registry:register",
"red.registry:supersede",
"red.registry:*",
"*",
"admin:*",
"vault:*",
"kv:*",
"policy:*",
];
#[test]
fn no_duplicate_names() {
let mut seen = HashSet::new();
for entry in ACTIONS {
assert!(
seen.insert(entry.name),
"duplicate action name in catalog: {}",
entry.name
);
}
}
#[test]
fn covers_historical_allowlist() {
let names: HashSet<&'static str> = ACTIONS.iter().map(|e| e.name).collect();
for action in HISTORICAL_ALLOWLIST {
assert!(
names.contains(action),
"catalog missing historically-accepted action: {action}",
);
}
}
#[test]
fn historical_allowlist_still_validates() {
for action in HISTORICAL_ALLOWLIST {
assert!(
is_valid_action(action),
"action {action} was accepted before the catalog and must still validate",
);
}
}
#[test]
fn has_at_least_one_deprecated_entry() {
let count = ACTIONS
.iter()
.filter(|e| matches!(e.lifecycle_state, LifecycleState::Deprecated { .. }))
.count();
assert!(
count >= 1,
"catalog must demonstrate the Deprecated lifecycle state with at least one entry",
);
}
#[test]
fn removed_entries_are_rejected() {
for entry in ACTIONS {
if matches!(entry.lifecycle_state, LifecycleState::Removed) {
assert!(
!is_valid_action(entry.name),
"Removed entry {} must not validate",
entry.name,
);
}
}
}
#[test]
fn lookup_finds_known_entries() {
assert!(lookup("policy:put").is_some());
assert!(lookup("definitely-not-an-action").is_none());
}
}