mklang platform
Public security document

Security is a boundary, not a badge.

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.

Current posture Managed onboarding is open One production host. No SLA. Reviewed 2026-09-09
01 / enforced properties

The controls we can point at.

Nine invariants are stated in the repository, and each is enforced by code and by named tests. An invariant with no test is an intention.

InvariantWhat it means in practice
No credential is ever written unencryptedEnvelope encryption with a per-tenant data key. If the key encryption key cannot be loaded, the service does not start; there is no fallback or development bypass.
No secret enters an mklang blackboardA step never receives a credential. The connector runtime resolves it at call time, and platform checkpoints are sealed before they reach the database.
An unverified webhook starts nothingHMAC covers the raw body and timestamp. Missing, wrong, expired and unknown signatures receive the same answer.
An absent credential is a denialNo configuration makes authentication optional. A route test walks the registered surface so a new endpoint cannot be unprotected by omission.
Cross-tenant reads require two independent failuresApplication scoping and forced Postgres row-level security both protect tenant data.
Every connector response is external dataIt is marked before it can reach a judgement, so provenance and control-flow taint can act on it.
Logging is allowlist-basedA field a vendor adds tomorrow is redacted by default rather than logged by default.
A data-plane credential is reduced to what that plane can useThe control plane renews credentials; a runner makes vendor calls. Refresh tokens and client material used only to obtain the next token stay behind the broker.
An external effect is claimed before the call and confirmed afterAn ambiguous claim stops the run. Durable execution is at-least-once, so this is what stands between a retry and a second email.

A change is audited in the same database transaction that made it — written afterwards, a trail claims things that never committed.

02 / threat model

What the boundary protects.

The model names both sides of the boundary. A threat that is outside it is disclosed plainly, not hidden behind a general security claim.

In scope

  • Cross-tenant data access. Application scoping, row-level security and per-run compute isolation address the failure that ends the company.
  • Credential compromise. Per-tenant keys limit the blast radius, and connection lifecycle states make revocation visible.
  • Prompt injection reaching an effect. External data is tracked structurally; the platform refuses an effect reached through a tainted decision.
  • Hostile workflow content. Signed bundles are verified before execution, including every file in the signed manifest.
  • Resource exhaustion. Tenant quotas bound concurrency, runs and tokens.

Out of scope

  • Compromise of a model provider. That inherited trust is disclosed, not defended against.
  • Compromise of a vendor API a connector calls.
  • A tenant's own users acting within their permissions. Authorization bounds action; 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.

Python code runs in an ephemeral container with no host credentials, a bounded lifetime and no network. JavaScript and non-empty network allowlists fail closed because those shapes are not implemented.

03 / data handling

Where information sits.

The platform processes customer workflow data on their instructions. The rows below describe storage boundaries, not a promise that data never leaves the platform.

ClassWhereAt restRetention
CredentialsControl planePer-tenant DEKUntil revoked; tenant-wide erasure is operational
Step payloads, blackboards, checkpointsData planePer-tenant data-plane keyNo automated expiry today
Run metadataControl planeDatabase-levelNo automated expiry today
Execution tracesData planePer-tenant data-plane key30 days by default; configurable from 1 to 365 days
Audit eventsControl planeDatabase-levelAppend-only; no automated expiry today

Hosting is in Germany. Backups are verified before they are called good, restoring from them has been rehearsed on a throwaway host, and copies that leave the host are encrypted under a key the host does not hold.

The model provider is a sub-processor: a judgement sends the data it is about to whichever provider that deployment is configured for. A customer supplies the provider connection; it is never step material.

04 / responsible disclosure

Find a problem? Give it a quiet path.

Please test only something that is yours. Do not open a public issue. The live host is not a public test target.

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.

Our current commitment
Acknowledgementthree business days
Assessment with a severity and a decisionten business days
Remediation, mitigation, or a written rationalecommunicated with the assessment
Creditif you want it
05 / review us

Evidence before reassurance.

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.

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, a decision record, or 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.