resksecure · Documentation
Bitmask-driven logits filtering
resksecure maps an integer capability mask to a policy of banned phrases and tool gates, then suppresses those tokens during generation. It is the low-level RBAC-to-decoding bridge behind ReskSafety's local mode.
On this page
Licence first
Not open source
resksecure is distributed under the RESK Software License v1.1. Personal, educational, research and evaluation use is free; use by a professional entity requires a commercial agreement. Read LICENSE in the repository before deploying it.
Install
pip install resksecure # requires Python 3.13+
pip install -e . # from the repository
Dependencies: resklogits, torch, transformers, pyyaml. The published 0.1.0 wheel predates the current dependency floors; a source install follows pyproject.toml.
Policy and bitmask model
A bit is a capability. Bit i is granted when mask & (1 << i) != 0. Roles are just integers you compute and pass in — there is no role store, no JWT handling and no mask builder.
read_email = 0
send_email = 1
read_sql = 2
contributor = (1 << read_email) | (1 << send_email) | (1 << read_sql) # 7
viewer = 1 << read_email # 1
Each mask maps to one policy in the YAML file (exact integer lookup, with a default: true fallback). A policy carries:
rules— phrases to ban during generation (hardmode) with an optional penalty;tools— tools and the bit each requires, plus trigger phrases that become hard-banned when the bit is absent;strict— force EOS when generation approaches a banned prefix.
Quick start
from resksecure import BitmaskLogitsProcessor, load_policy, verify_tool_action
policy_set = load_policy("src/resksecure/config/example_policy.yaml")
processor = BitmaskLogitsProcessor(
mask=7,
model_name="microsoft/phi-2",
tokenizer=tokenizer,
policy_set=policy_set,
device="cpu", # default is "cuda"
)
outputs = model.generate(
**tokenizer("List all users:", return_tensors="pt"),
max_new_tokens=50,
logits_processor=[processor],
)
processor.reset() # mandatory between independent generations
verify_tool_action("send_email", user_mask=7, policy_set=policy_set) # True
verify_tool_action("send_email", user_mask=1, policy_set=policy_set) # False
Only batch size 1 is supported. The automaton state persists across generate() calls until reset().
Policy file
version: "1"
policies:
- mask: 7
name: contributor
strict: false
rules:
- phrase: "drop table"
mode: hard
tools:
read_email:
required_bit: 0
send_email:
required_bit: 1
trigger_phrases: ["send_email("]
- mask: 23
name: auditor
strict: true
default: true
A tool is allowed when its bit is set; when it is not, its trigger_phrases are appended to the hard ban list, so the model cannot emit the call prefix. verify_tool_action() re-checks the same bit after generation, as defence in depth.
PolicyWatcher polls the file and exposes current_policy; TrieCache caches built automata for 300 seconds, keyed by (mask, model_name) — invalidate it when you change the policy.
API
| Symbol | Purpose |
|---|---|
load_policy(path) | Parse a YAML policy set. Missing mask/required_bit raises KeyError. |
PolicySet.get_policy_for_mask(mask) | Exact lookup with default fallback; None means the processor is inert. |
BitmaskLogitsProcessor | Applies hard bans, soft penalties and EOS forcing during decoding. |
verify_tool_action(tool, mask, policy_set) | Post-hoc tool authorisation; unknown tools are denied. |
get_allowed_tools(mask, policy_set) | List tools granted by a mask. |
build_ac_from_policy, get_or_build_ac | Automaton construction helpers. |
PolicyWatcher | Background hot reload of the YAML file. |
TrieCache | TTL cache for built automata. |
Limitations
- Alpha maturity. 0.1.0, thin behavioural tests; pin the version.
- Unknown mask = no filtering. If no policy matches and there is no default, generation proceeds unfiltered with no warning. Fail closed in your code by rejecting unknown masks.
biasmode is a token-level penalty. The YAMLpenaltyis applied to every matched token, so a soft rule nudges rather than blocks; usemode: hardwhen the phrase must never appear.- Hard mode is a token blacklist, not a prefix-aware filter: those tokens are discouraged everywhere.
- Hot reload does not rebuild processors; read
watcher.current_policyand construct a new processor. - Single sample only. Batch size above 1 raises
NotImplementedError.
Links
- GitHub · PyPI · project page
- resk-logits for the underlying processors