Security and Architecture
Written for the person who has to approve this, not for the person buying it. It includes what the design does not defend against, because a security page that only lists strengths is not telling you anything.
Last updated: 23 October 2026
Contents
1. The shape of a deployment
KRISID is installed inside your own environment. There is no multi-tenant service and no shared instance: one customer, one installation, one database.
| Component | What it is |
|---|---|
| Application | A single Python 3.12 service, run under systemd, listening on loopback |
| Database | PostgreSQL 16 or later — your own, or a managed instance such as RDS or Cloud SQL |
| TLS | Terminated by nginx or Caddy in front of the application |
| Extensions | None. No pgvector, no AGE, no pg_trgm — nothing for a platform team to approve |
| Operating system | Any current Linux; Ubuntu 24.04 and RHEL 9 are what we run |
The application speaks plain HTTP on 127.0.0.1 and is designed to keep doing
so. TLS, client certificates, IP allow-listing, WAF rules and SSO at the edge are all
your reverse proxy's job, where your existing standards already apply.
2. What leaves your network
Nothing goes to us. There is no telemetry, no usage reporting, no licence check over the network, no crash reporting and no update check. The repository holds descriptions of your data, and often that metadata is itself regulated — a product that quietly exported it would be unusable in the sectors we work in.
Outbound connections exist only where you configure them, and only to systems you name:
- The sources you connect for harvesting — Snowflake, Databricks, Microsoft Fabric, Power BI, Tableau, an OpenLineage endpoint
- An SMTP server you supply, for invitations and notifications
- Any webhook a workflow action calls, which is a named connection you create
Harvesting reads metadata — names, structures, comments, tags, row counts and the like. Connector credentials are yours and scoped by you; we recommend a read-only role.
3. Identity and sessions
The only way in is a password and a session. An earlier development header that could assert an identity was removed, and the verify suite checks that it no longer changes anything even when presented alongside a real session.
- Passwords are hashed with scrypt — N=214, r=8, p=1 — salted per person, using the standard library rather than a third-party dependency. They are hashed, not encrypted: one-way, and unreadable even to somebody holding every key the application has.
- Session and invite tokens are never stored. Only their hashes are. A database dump does not yield a usable session.
- Signing out ends a session; sessions expire; changing a password requires the old one and ends every other session for that person.
- An invitation sets a password once, and a short password is refused.
- Switching a person off ends their sessions, and nobody can do it to themselves.
The built-in administrator ships with a known password and the application warns on screen for as long as it remains unchanged. The seeder never resets it afterwards.
4. Authorisation
Permissions are held either over the repository or over a particular asset through a role on a domain, so a domain owner decides for everything that domain owns. Every write goes through the same checks regardless of whether it came from a screen, the API or a workbook upload — the verify suite tests the screen path and the write path separately for exactly this reason.
Where a workflow governs an asset type, an edit becomes a change request rather than a write, and applying an approved request runs as the workflow rather than as the approver, so every database-level refusal still fires. Four eyes can refuse the person who raised a request even when they hold the deciding role, and an approval is refused outright if the value moved after the request was raised.
A “view as” facility lets an administrator see what somebody else sees; while it is active every write is refused.
5. Secrets
One secret has to be presented to another system rather than verified — the SMTP
password — so it cannot be hashed. It is encrypted, and the key lives
outside the database, in KRISID_SECRET_KEY in the service
environment. If the key were in the database, a copy of the database would be a copy of
the key and the encryption would be decoration.
That password is not returned by the API, not readable in the database, and decrypts only with the key held outside it. The threat this actually defends against is a dumped database, a shared backup or a screenshot of a table.
6. Audit and logging
- Every change records the attribute, the value before and after, and the person — including who was made responsible for what, and by whom.
- History is never trimmed. Pruning explicitly leaves it alone and says so.
- Seven separate log channels each answer from where the fact already is, so a refused sign-in appears in the sign-in log under a person's name.
- Tokens, cookies and passwords are stripped from everything written to the logs.
- Logs are visible only to named readers; holding an administrative role is not sufficient on its own.
7. Licence and file integrity
Each installation carries a licence signed with Ed25519 naming your organisation and the hostnames that copy may serve. The public half is a constant in the application, so a licence cannot be forged — editing a hostname breaks the signature and the application reports it as forged rather than as valid. The check is local and per request; loopback always works, because a licence check that locks an administrator out of their own server is the outage it was meant to prevent.
manifest.json is a signed list of the SHA-256 of every shipped file. The
licence screen and the doctor command compare what is on disk against it and
name anything that no longer matches. This is reported, not enforced — a
changed file is sometimes a support patch applied at 3 a.m. with our help. What matters is
that nobody can change one quietly.
Upgrades are checked rather than asserted: the suite proves that a fresh install and an upgraded one produce an identical database — the same columns, constraints and indexes.
8. What this does not defend against
Said plainly
Somebody who already has the machine has the key file and the database both. Nothing a single-process application can do changes that, and pretending otherwise is worse than saying it.
Also worth being clear about:
- Nothing stops a licensee redistributing the software. A contract does. Host binding and tamper-evidence raise the effort and remove deniability; they are not DRM.
- An expired licence warns and keeps working. A hostname that is not yours means somebody moved the software; a date that has passed usually means a purchase order is slow, and taking a client's repository down over that is not something we are willing to do.
- We have not had an external penetration test. If your procurement requires one, say so early and we will arrange it as part of the engagement rather than claim one we have not had.
- There is no SOC 2 or ISO 27001 certification. Those certify an operator of a hosted service; KRISID runs in your environment, under your controls and your certifications. If you need evidence about our own practices as an implementation partner, ask and we will answer specifically.
9. Reporting a vulnerability
Write to contact@krisid.com with as much detail as you can. We will acknowledge it, tell you what we find, and credit you if you would like to be credited. We will not pursue anybody who reports a genuine issue in good faith, stays within their own account, and gives us a reasonable opportunity to fix it before disclosing it publicly.
Published by KRISID · 23 October 2026. The behaviours described here are checked by the product's own verification suite. Where a claim is about our practice rather than the software, it says so.