Skip to content

Concepts

OpenMaskit is small but has a handful of independent moving parts. Here's each one in isolation.

Aliases

An alias is a stable placeholder string that replaces a sensitive value in a tool response. Aliases follow the pattern {prefix}_{counter} — e.g. host_1, email_2, api_key_3.

Two properties make aliases useful:

  1. Stable across calls. The same upstream value always maps to the same alias for a given server, so the model can reason about it consistently across tool calls.
  2. Reversible. When the agent passes an alias back into a subsequent tool call, OpenMaskit swaps in the real value before forwarding upstream.

Aliases are namespaced per server. Two servers can independently hold the same alias (postgres:host_1 and staging-db:host_1 are different mappings). The persistent table primary key is (target_name, alias).

You can browse current mappings under Servers → {server} → Mappings.

Masking rules

A masking rule says: "in tool T's response, take the value at field path F and replace it with an alias prefixed P."

Three pieces:

  • Tool name — exact match against the advertised tool name (case-sensitive).
  • Field path — dot-notation into the response's structuredContent. Lists are fanned out: categories.id against {"categories": [{"id": "a"}, {"id": "b"}]} matches both.
  • Actionmask (replace with alias) or strip (remove the field entirely; the model never sees it, masked or not).

Rules apply to structuredContent (parsed JSON the upstream returns alongside text) and to text blocks that parse as JSON or Python-repr literals. Plain text blocks fall through to mappers — see below.

Output mappers

A mapper is a function that runs on the response before masking rules apply. Two kinds:

  • regex_replace — runs a regex against text blocks. Useful for pulling a substring out and aliasing it (e.g. matching host=([\w.-]+) and aliasing the captured group).
  • json_field_mask — same idea as a masking rule, but applied at the mapper stage. Lets you mask fields inside text blocks that happen to contain JSON, not just structuredContent.

Mappers exist because real-world MCP servers don't always return clean structured JSON. Sometimes the sensitive value is buried in a free-text "Result:" string. Mappers give you a hook to surface and alias it.

Guardrails

A guardrail is a pre-flight check on tool call arguments. If the unmasked arguments match a guardrail's pattern, OpenMaskit refuses to forward the call upstream and returns a -32602 error to the agent.

Three match types:

match_type Behavior
contains Substring match against the named argument's string value.
equals Exact match.
regex Regex search.

If argument_name="*", the pattern is applied recursively to every string in the argument tree.

Common uses:

  • Block DROP TABLE, DELETE FROM ... WHERE 1=1, TRUNCATE patterns against an SQL execution tool.
  • Block destructive HTTP verbs against an open-ended HTTP tool.
  • Block argument values that resolve to production resources you don't want the agent touching.

Blocked calls land in the audit log with status="blocked" and a block reason.

Injections

An injection silently adds, overrides, or appends an argument value before OpenMaskit forwards the call upstream. The agent never sees the injection happen.

Three modes:

Mode Behavior
set Always override the argument with the configured value.
default Only set the argument if absent.
append Append to a string or list argument.

Common uses:

  • Force limit: 50 on a tool the agent likes to call with limit: 10000.
  • Set dry_run: true on a destructive-looking tool while you're still tuning prompts.
  • Pin database: "staging" regardless of what the agent asks for.

Hidden tools

A hidden tool is removed from the agent's view entirely. The tool does not appear in tools/list, and if the agent somehow calls it (from memory of a prior session), OpenMaskit returns METHOD_NOT_FOUND.

Hide via Servers → {server} → Tools. Useful for upstream servers that expose 50 tools when you only want the agent to see 5.

Audit log

Every terminal-state tool call is recorded in ~/.openmaskit/traffic.db. Per row:

  • Timestamp, server, tool name, request id, status (ok / error / blocked), duration.
  • Masked args and masked response — plaintext (safe to read without the key).
  • Unmasked args and unmasked response — Fernet-encrypted at rest. The dashboard decrypts on the fly to show you what really flowed.

The table is row-capped (default 10,000 rows; configurable via OPENMASKIT_TRAFFIC_MAX_ROWS). Oldest rows beyond the cap are deleted every five minutes.

View under Servers → {server} → Traffic.

The request pipeline

When an agent calls a tool through OpenMaskit, requests flow through these stages in order:

  1. Hidden tool check — block with METHOD_NOT_FOUND if the tool name is hidden.
  2. Unmask arguments — replace aliases in the args with real values.
  3. Guardrail check — refuse the call with -32602 if a guardrail pattern matches.
  4. Injection application — silently set / default / append argument values.
  5. Forward upstream.

Responses flow back through:

  1. Mappers — regex and json_field_mask transformations.
  2. Masking rulesmask aliases values; strip removes fields.
  3. Forward to agent.

Where things are stored

OpenMaskit keeps two SQLite databases in ~/.openmaskit/:

  • store.db — masking config (rules, mappers, guardrails, injections), alias mappings, hidden tool list, marketplace and custom server registrations.
  • traffic.db — the audit log. Separate file so rotating or corrupting one cannot kill the other.

OAuth tokens live in ~/.openmaskit/oauth/{server_id}.json, encrypted with the same key.

The encryption key lives at ~/.openmaskit/.key and can be overridden by the OPENMASKIT_ENCRYPTION_KEY env var if you'd rather keep it elsewhere (a real secret manager, an HSM-backed file, etc.).