Afunana
Afunana Documentation

Security & Governance

What a security review and an AI-governance review will each ask for.

The administration screen showing model spend over time, broken down by workload

Every model call is metered, and the ledger is what the record is built from.

← All docs

Afunana reads the legacy source that runs the business — often an institution's most sensitive asset — so security is a design constraint, not a bolt-on. The platform runs inside the customer's own network; nothing about the analyzed systems leaves that boundary unless the customer explicitly configures an outbound integration (an LLM provider, a SIEM, or the source system). Compliance & Standards maps these controls to OWASP, ISO/IEC 27001, and SOC 2.

Signing in, and what each person can see

Two questions are answered separately on every request: what this person is allowed to do, and which systems they are allowed to see it for. Both have to pass. Sessions expire and can be revoked, people can sign in through the organisation's own identity provider, and AI tools and scripts go through the same checks as the browser.

Authentication

Sessions are carried by HS256-signed JWTs. The signing key must be at least 256 bits and is a bootstrap secret — from a mounted container secret or the environment, never the database — and the process refuses to boot if it is unset or too short.

Passwords are hashed with bcrypt (per-password salt) with enforced complexity, and are never logged or stored in plaintext; reset tokens are kept only as SHA-256 hashes with a short expiry. Against enumeration, the forgot-password flow returns a generic response whether or not the address exists, and login equalizes response timing for unknown-user and wrong-password outcomes. Repeated failed logins lock the account; that lockout is persisted, survives a restart, is audited, and guards the SSO callback path.

Session management

Two independent clocks bound every session: an idle timeout that expires a token after a configurable period of inactivity, and an absolute lifetime (configurable, shipped at 8 hours) enforced on every refresh and not extendable. Revocation is per-token on logout, checked on every authenticated request, and per-user by an administrator, who can also force every user to re-authenticate. Both are audited, and logout propagates across browser tabs.

Roles, and the data a role reaches

Authorization answers two independent questions, and both must pass before a request succeeds.

What may this person do? Every user holds one role, and a role is a named bundle of permissions — collection.view, collection.build, collection.delete, source.write and so on. Six roles ship: admin (everything), pgmr (defines and builds collections, edits source, administers nobody), qa (adds the review screens and the audit log), analyst (reads and asks questions), user (the default), and viewer (read-only, with no chat, so it spends nothing on the LLM). A single permission can be granted or denied to one person on top of their role, for the exception that does not justify a new role.

Which data may they see? Separately, a person is given collections — either one at a time, or a whole application, which reaches every collection filed under it and under anything nested inside it, including collections created later. The role decides what kind of operation is allowed; this decides which data it may touch. Neither substitutes for the other: a permission to delete a collection does not say which collections exist for that person.

An application is a named business division of the estate — Life insurance, and inside it Life calculations. Applications nest, and a grant travels downward only: holding Life holds Life calculations, and holding Life calculations holds nothing above it.

Access can be handed out by the customer's own directory rather than maintained here. A mapping row of the form "anyone in group Claims-IT holds the Claims application" is re-evaluated at every login, and it withdraws access as well as granting it — so a person who transfers out of a department loses that department's documentation at their next sign-in, with nothing entered on our side.

The backend is authoritative: every protected endpoint declares its guard server-side and requests without it return 403; frontend route guards are usability only. The permission check fails closed — if the tables cannot be read the answer is no, because admitting somebody on the strength of a broken query is how an authorization check becomes decorative. An administrator cannot be denied the two permissions that would prevent them from reversing the denial.

Single sign-on

Afunana supports OpenID Connect (OIDC), used with Azure AD (Microsoft Entra ID), Okta, Google Workspace, and generic providers. ID tokens are validated against the provider's published JWKS keys with issuer, audience, and expiry checks; one whose signature cannot be verified is rejected, with no fall-through to an unverified decode. Discovery is SSRF-guarded — a discovery or JWKS URL must be HTTPS and resolve to a public address, so it cannot be pointed at internal services. Each login mints a random, single-use, time-boxed state parameter and a nonce, defeating CSRF and replay.

Afunana also supports SAML 2.0 as a service provider, for the many enterprises that federate applications to an internal ADFS or a SAML tenant rather than issuing OIDC registrations. Assertions are verified against the certificate the administrator configured, never one carried inside the response, and everything read afterwards comes from the subtree that signature actually covered — which makes XML Signature Wrapping structurally impossible rather than a check that has to be remembered. Beyond the signature, an assertion is accepted only if its issuer, audience, recipient and destination all name this install, it is inside its validity window, it answers an authentication request this install issued, and its ID has not been seen before. XML is parsed with entity resolution, DTD loading and network access disabled, and a document carrying a DOCTYPE is refused outright.

Both protocols resolve through one identity layer, so a person is joined to their provider by its immutable subject rather than by email, and directory group membership can grant roles and collection access as data an administrator edits rather than a code change. Auto-provisioning is optional in both and its default role can never be admin, either can be restricted to an email-domain allow-list, and every step is audited.

Request defences

For the local (non-SSO) session, CSRF defense uses Origin validation on every mutating request, checked against the configured CORS origins, the frontend URL, and the request's Host header; a mismatched or missing Origin is rejected. Rate limiting is per client and returns HTTP 429 when exceeded, strictest on the authentication endpoints — it throttles volume from an address, while lockout stops guessing against an account.

Security headers

The application sets these headers in middleware, not the proxy, so they apply even behind a customer's own reverse proxy.

Header Value Purpose
Content-Security-Policy Restrictive policy Limits script/style/connect sources
Strict-Transport-Security max-age=31536000 Forces HTTPS for one year
X-Frame-Options DENY Prevents clickjacking
X-Content-Type-Options nosniff Prevents MIME sniffing
Referrer-Policy strict-origin-when-cross-origin Limits referrer leakage

The audit trail

Every security-relevant action is written to a log the database will not let anyone edit or delete, and each record is chained to the one before it, so a change to any historical record can be detected. Nothing is forwarded outside the machine unless an administrator turns it on.

The audit trail

The audit store is INSERT-only and tamper-evident. Each record's integrity hash covers the previous record's hash plus its own identifying fields, forming a SHA-256 hash chain: altering or deleting any historical record breaks the chain from that point forward, detectably. Immutability is enforced at the database layer, not just in application code — a trigger blocks any modification or deletion, and the only writer that may bypass it is the scheduled retention purge, which does so briefly and under audit.

Events are categorised as authentication, authorization, data access, chat, administration, system, and integrity — each attributed to an actor, IP address, user agent, target, and outcome. Chat query text is logged only when explicitly enabled, off by default for privacy.

Retention is configurable and defaults to 365 days. A scheduled daily purge removes only records past that window, and where SIEM forwarding is enabled it removes only records already confirmed forwarded — so nothing is discarded before it has reached the customer's own retention system.

Events can be forwarded to a customer SIEM in real time, before the local insert, precisely because the immutability trigger forbids amending a record afterwards. Transports are syslog (RFC 5424) over TLS or an HTTPS webhook; formats are CEF or JSON. A scheduler verifies chain integrity hourly and in full daily, reporting where the chain first breaks; failures raise a critical event that forwards to the SIEM.

Outbound log streams

Two independent outbound doors exist, and both are OFF by default — nothing leaves the box unless an operator turns it on. They are configured through different mechanisms on purpose:

Both doors support TLS and mutual TLS, field-level redaction and pseudonymization before egress, and multiple destinations. They share one field set and identity, so a receiver can correlate an audit event with the application-log lines from the same request.

Secrets, encryption and hardening

The source code Afunana reads is often the most sensitive asset an organisation holds, and it stays inside the organisation's own network. This section covers where credentials come from, what is encrypted in transit and at rest, how the installation itself is hardened, what an air-gapped site gives up, and how machines rather than people are authenticated.

Secret management

Secret resolution follows two deliberately different precedence chains. Bootstrap secrets — database connection settings, the token signing key, the server's role designation — come from a mounted container secret or an environment variable and never from the database: they unlock the database itself, so they cannot depend on it. Startup fails loud if one is missing.

General secrets — API keys, SMTP credentials, SIEM tokens — resolve through a layered hierarchy: an external secret vault (CyberArk CCP or HashiCorp Vault) first when configured, then a writable on-box layer allowing rotation from the admin UI with no restart, then mounted container secrets, then database configuration, then the environment. Secrets are never logged, and sensitive values are masked in audit detail.

Encryption

In transit. TLS everywhere: the bundled proxy terminates TLS with automatic certificates and enforces HSTS (max-age=31536000), an external proxy to the same standard, and database connections and outbound integrations alike require encrypted transport.

At rest. SQL Server Transparent Data Encryption (TDE), AES-256. In bundled-DB mode TDE is automatically enabled, encrypting files and backups. In external-DB mode the database is the customer's, so TDE, or an equivalent at-rest control, is the customer's responsibility to configure; Afunana cannot enable it for them. This matters for audit evidence: only bundled-DB deployments can point to Afunana as the party that enabled at-rest encryption.

Platform hardening and vulnerability management

The application runs as an unprivileged service account, never root; the production image excludes build tooling; data services are reachable only on the internal container network; and in bundled mode it uses a least-privilege database login.

Every deploy runs dependency CVE scanning for Python and JavaScript plus static analysis of Python source; scans are non-fatal, recorded but not blocking. The code-quality check catalog lives in configuration rather than code, so changes to it are governed by the same admin authorization and audit logging as all other configuration.

On-premise and air-gapped operation

Afunana runs inside the customer's perimeter with zero vendor access. In fully air-gapped mode, every LLM call routes to a local model, embeddings come from a local open-source model, and the platform installs from an offline image bundle against an internal registry mirror. No data — and no LLM prompt — leaves the box.

AI tools and the API

The Model Context Protocol server (over stateless Streamable HTTP) is protected by OAuth 2.0 Authorization Code with PKCE, so external assistants connect under the same identity model as the web UI. PKCE (S256) is required on every flow, every request is bearer-JWT validated, and the implementation follows RFC 8414, RFC 9728, and RFC 7591.

The server is mounted at /mcp (Streamable HTTP transport); a wrapper rejects any request without a valid bearer JWT before the MCP app is reached. Beyond bearer validation, each tool invocation is scoped to the collections carried in the caller's token and refuses anything outside that list, so an MCP client cannot read another tenant's collection.


Which AI models run, and who approved them

Afunana uses language models to write the documentation. Most organisations running it therefore have to be able to answer three questions: which models are involved, what they are given, and who inside the organisation approved that. Afunana keeps those records itself and generates them from the running software, so they describe what the product is actually configured to do rather than what a policy document says it does.

Nothing here is a legal opinion, and no software can decide that a requirement has been met. What it does is hold the record: who decided, when, on what basis, and what the system reported at the time.

The register of AI uses

Every place the software calls a language model is listed. The list is generated from the router — from what the software actually does — so it cannot claim a use that is not configured, and it cannot omit one that is. Each entry carries the technical half and the governance half side by side:

Who is accountable

Accountability for what an AI system does cannot be delegated to a supplier, and under most financial-sector supervision it cannot be limited by contract either. The roles a deployment needs are derived from how it was classified, and each is held by a named person with a date and an appointer. An appointment nobody has confirmed is recorded as unconfirmed.

Classification and impact assessment

Each use is placed in a risk tier by answering five questions in the order the EU AI Act resolves them: prohibited practice first, then the high-risk uses listed in Annex III, then transparency, then whatever is left. The first answer that forces a tier decides it — this is not a score — and the answers are stored alongside the conclusion, so the decision can be re-read next year.

Every use above minimal risk then carries an impact assessment covering what it touches, what it is given, how wrong it can be, and what stands in the way. It is scored on a fixed scale so two years can be compared, it names who carried it out, and it expires. Two rules apply: an unanswered question counts as its worst answer, so a half-filled assessment reads as high risk rather than low; and the person who carried out an assessment can never sign it off.

The part the software enforces

Configuration says which model an administrator wants a role to run. A separate approved list says which models are permitted, and the router resolves one against the other at call time — an unlisted model is skipped and named in the log. Both halves are needed for enforcement to begin, so a list somebody is still drafting cannot start refusing work.

Changes can be made to require a second administrator. The change waits as a request carrying the benchmark it was sent with, so the approver reads the evidence rather than taking the requester's word for it, and the person who asked for a change can never approve it. Applying a change is a separate step, and a request that has gone stale — because the value moved after it was raised — is refused rather than silently overwriting the newer value.

What you can hand over

What is switched on when it arrives

Nothing. The approved list ships empty, and an empty list means no policy rather than nothing permitted. Both gates ship off. No framework is declared in scope until somebody chooses one. A governance feature that broke an installation the day it shipped would be switched off before anybody read it, and a switched-off control leaves no trace at all.


Standards and regulations

Two sets of rules apply to a product like this: those that govern the software itself, and those that govern the use of AI. Both are held the same way. Each requirement is a row carrying two things side by side — the position your own people recorded, and what the running system reports about the same question. The two are never merged. Where a requirement is recorded as met but the software reports otherwise, the row is flagged and counted: either the evidence lives outside the software, or the record is wrong.

For the software itself

For the AI

Framework What it covers
ISO/IEC 42001:2023 The certifiable AI management standard. Clauses 4 to 10 and all 38 Annex A controls, exportable as a statement of applicability.
EU AI Act Article by article, including the twelve deployer obligations of Article 26 broken out separately. Each row says whether the deployer or the provider carries it.
NIST AI RMF 1.0 Govern, Map, Measure and Manage, at category level — the vocabulary most enterprise risk functions already speak.

Sector and national rules are carried the same way, written as their own requirement set and kept beside these rather than folded into them. Ask us what already exists for your regulator. Where an instrument has been announced but not issued, it is labelled as announced: preparing for it is useful, claiming compliance with an unpublished text is not.

What these mappings are, and are not

They state, for each framework item, how Afunana's implemented controls address it: an engineering self-assessment to help security, audit, and GRC teams slot the platform into their own control environment. They are not a certification or an attestation. Afunana is mapped to these frameworks; it is not certified against them. Third-party certification and a SOC 2 Type II examination are in progress, not complete. Read "addresses," "supports," or "mapped to" as an implementation claim to be verified against your own deployment — never as independent assurance.

The complete item-by-item mappings — the OWASP Top 10 categories, the ISO/IEC 27001:2022 Annex A controls, and the SOC 2 common criteria plus availability and confidentiality — are documented and available on request.

Two limits are stated plainly rather than mapped away: MFA is delegated to the identity provider via OIDC/SSO rather than implemented in the platform, and at-rest encryption is automatic only in bundled-DB deployments.

What the automated checks will not do

Alongside each requirement the software runs a narrow factual check against itself: is the change gate on, is the register complete, are audit records kept for at least the six months Article 26(6) asks for. Those checks report one of four states, and the fourth matters most: cannot tell. If a ledger is empty, a period cannot be read, or no deployment plan exists yet, the check says so rather than counting itself as a pass. An all-green compliance screen produced by checks that could not run is the failure this apparatus exists to prevent.

The compliance surface

An admin-only compliance interface turns these controls into operations an auditor can actually run: filtering audit events by type, actor, and severity; exporting the audit log as JSON or CSV (the export is itself audited); checking hash-chain integrity on demand; invalidating one user's sessions or all of them; exporting a collection's documentation, metadata, and chat history for ISO/IEC 27001 A.8.10 portability and deletion workflows; and a status summary of enabled controls.

How to read the status summary. These rows used to be literal true values in code. They are now derived, with an explicit unknown state rather than defaulting to green:

  • TLS in transit is read from the scheme of the configured public URL — https reports true, http reports false, an unset URL reports unknown. TLS terminates upstream at the proxy, so there is nothing in-process to measure; this is the best evidence available inside the application, and it is honest about being configuration-derived rather than a network probe.
  • Database connection encrypted is true because the application builds its own connection string with encryption on. Certificate validation is reported as a separate row, because trusting a self-signed server certificate means the connection is protected against passive capture but not against active interception — folding the two into one green tick would have overstated it.
  • Session revocation is probed by checking that the table the enforcement path actually reads exists, rather than asserted.

What still has to come from the customer's own evidence: the proxy's TLS configuration and cipher policy, and — for an external database — their TDE configuration. Afunana cannot observe either.