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:
- 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.
- 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.idagainst{"categories": [{"id": "a"}, {"id": "b"}]}matches both. - Action —
mask(replace with alias) orstrip(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. matchinghost=([\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 juststructuredContent.
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,TRUNCATEpatterns 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: 50on a tool the agent likes to call withlimit: 10000. - Set
dry_run: trueon 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:
- Hidden tool check — block with
METHOD_NOT_FOUNDif the tool name is hidden. - Unmask arguments — replace aliases in the args with real values.
- Guardrail check — refuse the call with
-32602if a guardrail pattern matches. - Injection application — silently set / default / append argument values.
- Forward upstream.
Responses flow back through:
- Mappers — regex and json_field_mask transformations.
- Masking rules —
maskaliases values;stripremoves fields. - 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.).