Loading
Two separate questions, and most security pages answer only the first. Below: how to report something, the properties this product enforces in code rather than in prose, and the honest answer to who has independently verified any of it.
The process lives in SECURITY.md in the repository. That file is the authority and this page is not a second copy of it: read it before you report, and if this page and that file ever disagree, that file is right.
The short version, so you know where you are going: not a public issue. Use GitHub’s private vulnerability reporting on the repository’s Security tab, which opens an advisory only the maintainers can see. A failing request, a policy file or a short script is worth more than a description of one.
SECURITY.md also states the acknowledgement expectation and asks you to say whether you plan to publish. Both are there rather than here for the same reason.
Summarised from SECURITY.md, which is the version that governs.
This project’s own code: the enforcement path, the policy engine, the audit chain and its verifier, the gateway and its authentication, the tenant isolation in src/nometria/tenancy.py, and the public playground on this deployment.
Concretely, a vulnerability is: getting an action through that the declarations should have refused, reading or writing another tenant’s data, forging or breaking the audit chain without the verifier noticing, escalating a token’s permissions, or making the control plane fail open without recording it.
Two things, both excluded because the project already says so in public and measures them rather than hiding them.
A prompt injection that a detector misses. Detection here is a speed bump, not a defence, held-out recall is published in the README, and an adaptive attacker gets most caught attacks through eventually.
An attack that gets through when the declarations are wrong. Containment is only as good as the tool declarations and capability grants behind it. A tool declared read-only that moves money is not contained, and the product says so.
On the hosted playground specifically, the terms draw the same line from the other direction: attacking the sandboxed agent is the entire point, attacking the infrastructure it runs on is not.
Three, each with the file that implements it. These are claims about code, which means they are falsifiable, which is the only kind worth making.
The obvious implementation, adding a tenant clause to every query, holds until the first person writes a new one and forgets, and the failure mode of forgetting is a silent cross-tenant leak that no test catches because the test wrote both rows itself. So the filter is attached at the session: a hook adds a tenant predicate to every ORM statement, which means a query whose author has never heard of the tenancy module is filtered anyway.
Two things make it hold rather than merely exist. With no tenant bound, queries resolve to the deployment’s configured organisation rather than to everything, so a gateway bug that forgets to bind shows a user an empty screen instead of another company’s data. And a model that escapes the filter raises at import time, so the mistake cannot reach a running system.
src/nometria/tenancy.py:15-24, 35-38; src/nometria/models.py:1669-1688
The playground rides on exactly this. A sandbox is a tenant whose org_id is its own id, so one visitor is separated from another by the same mechanism that separates two paying customers, with no playground-specific filter for anyone to forget.
src/nometria/gateway/playground_sessions.py:8-13
Immutable in most products means nobody built a DELETE endpoint. Here each entry’s digest covers its sequence number, timestamp, action, payload digest and the previous entry’s digest, with periodic checkpoints signed by a key held outside the application database.
The verifier detects mutation, deletion, insertion, reordering and checkpoint forgery, and it runs over exported rows with no database access and no shared state. That last part is what makes it worth anything: a third party can run it against an evidence package without access to our systems, so you do not have to take our word for the result. There is also no update or delete path for an audit entry anywhere in the codebase.
src/nometria/audit/chain.py:1-20, 56-64, 298-312
A control that fails open silently is indistinguishable from a working one: the same traffic flows, the same 200s come back, and the dashboard is green because the detector that would have raised the finding is the one that is down. This codebase’s position is that fail-open is legitimate and has to be visible, bounded, and impossible for some controls.
These four are the impossible ones. Their failure mode is a disclosure rather than an outage, so declaring one of them open raises in the constructor, not as a warning at runtime, because a setting that can be changed under pressure at three in the morning is not a guarantee.
src/nometria/availability.py:18-30, 55-64, 87-95
Everything else can fail open, and the default detector fail mode is open. When it happens, the request that ran without a control writes a degradation record, and open converts to closed after a declared time or share of traffic, because a control that has been open for an hour is not degraded, it is absent.
src/nometria/config.py:148-149; src/nometria/availability.py:21-26
There has been no third-party security audit. There is no SOC 2 report, Type I or Type II. There has been no penetration test by anyone. There is no ISO 27001 certification and no paid bug bounty. None of those things have happened, and it would be easy to imply otherwise with a badge and a vague sentence.
What exists instead: the source, the tests, a verifier you can run yourself against exported evidence, and published numbers for the detection layer including the ones that are unflattering. That is a weaker assurance than an audit and it is a different kind of thing, not a substitute. If you are evaluating this for something that matters, read the code, run the verifier, and price in that it has not been reviewed by anyone but its maintainer.
The playground has a stated limit worth knowing before you test it: its rate limiter is per process, so on a serverless deployment it bounds one instance’s share of abuse and does not bound a client spread across instances. The sandbox lifetime and the concurrent-sandbox cap are what actually bound the cost, and both are deployment-wide. That is written down in the module rather than implied.
src/nometria/gateway/playground_sessions.py:371-379