Skip to main content

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:

LayerQuestion it answersApplies 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:

RoleScope
adminSystem administration, user/role management, ingestion config, policy config
operatorRun ingestion, monitor pipelines, view dashboards
userQuery 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 (allowed or blocked),
  • 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:

  1. 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.
  2. Edit the customization surface — the lexicon and construction lists documented in Filter Policy → Rules-customization surface.
  3. 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.
  4. Monitor the audit log for the balance of blocked vs allowed decisions 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. :::