Security
This platform holds other people's credentials and runs LLM-driven workflows over their business data. This page states what is guaranteed, what is not, and how to report a problem. It is the public form of the security document that ships in the repository, and it is kept narrower than that document on purpose: nothing here may claim more than the source it derives from.
Reviewed 2026-08-06. Phase 1 — one production host, no customer on it.
Invariants
Eight invariants are stated in the repository, and each is enforced by code and by named tests. An invariant with no test is an intention, and the distinction is drawn in the source document rather than smoothed over here.
| Invariant | What it means in practice |
|---|---|
| No credential is ever written unencrypted | Envelope encryption with a per-tenant data key. If the key encryption key cannot be loaded, the service does not start — there is no fallback and no development mode that skips it. |
| No secret enters a model's context | A step never receives a credential; the connector runtime resolves it at call time. What the language writes into a checkpoint is sealed before it reaches the column. |
| An unverified webhook starts nothing | HMAC over the raw request body with the timestamp inside the signature, so a captured request stops working. A missing signature, a wrong one, an expired one and an unknown path are the same answer. |
| An absent credential is a denial | No configuration makes authentication optional, and a test walks every registered route so a new endpoint cannot ship unprotected by omission. |
| Cross-tenant reads require two independent failures | Application scoping and Postgres row-level security, forced. One of them being wrong is not enough. |
| Every connector response is external data | Marked as a type rather than a label, before it can reach a judgement — so provenance and control-flow taint have something to act on. |
| Logging is allowlist-based | A field a vendor adds tomorrow is redacted by default rather than logged by default, and the allowlist descends containers rather than blanking them. |
| An effect is claimed before the call and confirmed after | An ambiguous claim stops the run. Durable execution is at-least-once, so this is what stands between a retry and a second email. |
What is not defended against
Stated because a threat model without an out-of-scope list is a marketing document:
- Compromise of a model provider. Providers are chosen and data is passed to them; that trust is inherited and disclosed, not defended against.
- Compromise of a vendor API a connector calls.
- A tenant's own users acting within their permissions. Authorization bounds what they can do; it does not judge intent. The audit trail is the control.
- Physical and hypervisor security of the hosting provider.
- Availability guarantees. No service level agreement is offered until one is sold.
Where data sits
| Class | At rest |
|---|---|
| Credentials | Encrypted under a per-tenant key |
| Step payloads and model context | Encrypted under a key derived per tenant for the execution plane alone |
| Run metadata and audit events | Database-level. Audit is append-only and kept longer than the rest |
Hosting is in Germany. Every backup is verified before it is called good, restoring from it has been rehearsed on a throwaway host, and any copy that leaves the host is encrypted under a key the host does not hold.
Reporting a vulnerability
Report privately to
info@gianlucamazza.it, with mklang platform security in the subject. Encrypted
mail is welcome — ask in the clear first and a key comes back.
Please do not open a public issue, and please only test against something that is yours. There is a live host and it carries no customer; it is not an invitation.
| Acknowledgement | three business days |
| An assessment, with a severity and a decision | ten business days |
| A fix, or a written statement of why not | before any customer is exposed to the affected path |
| Credit | if you want it |
Reviewing us
A security review that asks to read the code is one of the two named triggers for publishing it. Ask, and that conversation starts rather than stalls.
Until then, one thing is worth knowing about the pages you are reading. Every factual statement on this site names a row in a register that ships beside the content, and each row names its evidence: a test that must still exist, a decision record, a phrase that must still appear in the security document this page derives from. The build fails when a statement outlives the thing it rested on, and it fails again when nobody has re-read a claim by the date on its row. Marketing prose rots quietly; this is the mechanism that stops these pages doing it between one review and the next.