Access Policy and Filter Manual
This is the user- and administrator-facing manual named in the TOR ("User Documentation: Access Policy and Filter Manual"). It covers the access model and the Policy Filter together, and explains how the two relate, what a user experiences, and what an administrator sees and does. For the internals of what the filter catches, see the Filter Policy Documentation.
Two independent layers
The platform protects a generated answer with two layers that are orthogonal and are both always on:
| Layer | Question it answers | Applies to |
|---|---|---|
| Access control (RBAC + ABAC) | May this user see this document / use this capability? | The request and the retrieved corpus |
| Government Policy Guard (the Policy Filter) | Is this generated answer policy-safe to emit? | The generated output, every time |
Access control decides what a user can reach; the Policy Guard decides whether the answer produced is allowed to be sent. They are enforced at different points and neither substitutes for the other — an answer built entirely from documents a user is authorised to see is still passed through the guard before it returns.
The access model
Access is RBAC + ABAC (ADR-0001; see Security and Governance). The working roles are:
| Role | Scope |
|---|---|
admin | System administration, user/role management, ingestion config, policy config |
operator | Run ingestion, monitor pipelines, view dashboards |
user | Query the chatbot, view own conversation history |
ABAC attributes — department and document classification — refine RBAC for document-level visibility. These attributes shape which chunks a query can retrieve, and so shape the grounding an answer is built from; they do not change the Policy Guard's behaviour, which is identical for every role.
What a user experiences
- A user queries the chatbot and receives a grounded, cited answer.
- If the Policy Guard blocks the answer, the user receives a fixed refusal
message instead of the generated text: "I cannot provide that response because
it violates the Government Policy Guard", with a citation appended when a
grounded fallback chunk exists (
src/aceh_rag/policy.py:96-121). - The refusal is reported as a complete response, not a truncated draft, so a
user is not invited to "retry with more tokens" against a policy block
(
src/aceh_rag/generation.py:216-224). - The offending output is never shown, and never stored. On the chat path the
blocked answer text is replaced by the refusal before the assistant message is
persisted (
src/aceh_rag/services.py:1185-1200, 1229), so the offending generation does not survive in the conversation history.
The guard cannot be turned off for a query, a user, or a role
(src/aceh_rag/policy.py:5). There is no per-user allowlist that skips it.
What an administrator sees
Every guard decision is written to the audit log — for allowed answers as much as blocked ones, so "allowed" is evidence too, not just silence. An administrator reviewing the audit trail sees, per answer:
- the action (
allowedorblocked), - the classifier version that decided it (
deterministic-rules-v2), - the policy categories that fired (
sara_inflammatory,jailbreak) (src/aceh_rag/services.py:1277-1289).
On the chat path the same fields are also attached to the assistant message
metadata under policy_guard (src/aceh_rag/services.py:1270-1274). On the
ungrounded POST /generate surface, the audit row is the only trace a
blocked generation leaves, so the request fails closed (returns a 503) if
the audit cannot be written — a guard decision that nothing can later account for
is treated as a failed control, not a cosmetic miss
(src/aceh_rag/generation.py:227-248).
:::note What the administrator does NOT see in the audit trail
Policy findings deliberately carry no matched text — the reason field records
"protected-group reference within an incitement construction", never the
offending sentence (src/aceh_rag/policy.py:81-84). Audit rows and logs must not
carry answer content. An auditor sees that a block happened and which
category, not the text that triggered it.
:::
What an administrator does
The admin role scope includes policy config. In practice an administrator's
interaction with the Policy Filter is:
- Read the rules. The entire guard is inspectable Bahasa/English/Acehnese
word rules in one file (
src/aceh_rag/policy.py) — there is no opaque model to interrogate. - Edit the customization surface — the lexicon and construction lists documented in Filter Policy → Rules-customization surface.
- Prove the change by running
make eval-policy(Makefile:141-142), which reports the catch and false-refusal rates and fails on any undocumented regression. A rule change is never shipped on judgement alone. - Monitor the audit log for the balance of
blockedvsalloweddecisions and for categories that fire unexpectedly.
The full change loop — evasion → corpus case → rule → gate → ship — is the Periodic Policy Updates process.
:::caution Human-owned deliverables Two Policy Filter deliverables in the TOR are client-delivery activities owned by humans, not tasks the engineering team or this manual completes on its own:
- the rules-customization working session with Pemprov Aceh stakeholders, where the boundary between blocking harm and refusing legitimate work is agreed using the measured rates, and
- security-administrator training.
They are named here so the reader knows they exist and who owns them; the material above is the reference those activities draw on. :::
Related
- Filter Policy Documentation — what the guard catches and what it deliberately does not.
- Periodic Policy Updates — how a rule changes without regressing.
- Security and Governance — data sovereignty, audit log, OCR governance.