Security

You are handing us keys. Here is what holds them.

Omniio sits between your agent and the servers it calls, which means it holds credentials that reach your code, your documents and your customers. This page says exactly how they are stored, who can spend them, what gets written down, and which assurances we cannot give you yet.

Your upstream credentials

An API token you paste into Omniio is encrypted with AES-256-GCM before it reaches the database. The key lives in the deployment’s environment, never in the database beside the data it opens, so a copy of the database on its own is a copy of ciphertext. Each record carries its own initialisation vector and authentication tag, which means a tampered row fails to open rather than opening into something else.

  • Tokens are decrypted in memory, for the one call that needs them, and attached to that call’s request to the upstream server. Nothing writes a decrypted token back anywhere.
  • OAuth grants to upstream servers are held per user and refreshed on demand. Your grant is never used to serve another account’s call.
  • Disconnecting a server deletes its stored credential outright — it is a delete, not a flag.
  • Credentials are not part of the audit trail. What is recorded is the arguments your agent sent and the result it got back; the token that authorised the call is added after that record is formed.

How a client gets in

Clients authorize against https://omniio.dev over OAuth 2.1 with PKCE required, and the token they receive is bound to https://mcp.omniio.dev as its audience. Nothing has to be pasted into a config file, and that token cannot be spent anywhere but the MCP endpoint — including on Omniio’s own read API, which takes a separate credential precisely so the audience binding means what it says.

Registration is dynamic, so every client that connects — the desktop app, an editor, a script — registers itself and gets its own credential. That is what makes a connection revocable on its own: cutting off one client does not disturb the others and does not make you re-authorize anything.

  • Every recorded tool call carries the client that made it, so “what did that machine do” is a filter rather than an investigation.
  • Revoking deletes that client’s tokens and its stored consent. Tokens are checked against the database on every request and nothing about them is cached, so revocation takes effect on the client’s next call — not at the end of some window.
  • Sessions in the dashboard are separate from client tokens. Signing out of the browser does not disconnect your agents, and disconnecting an agent does not sign you out.

See your connected clients →

If your team signs in through its own provider

A team on Business or Enterprise can put Okta, Entra or another OIDC provider in front of Omniio, and let it provision people over SCIM. Two credentials are involved, and they are not held the same way:

  • The SCIM bearer token is stored only as a SHA-256 digest. It is shown once, at the moment it is issued, and cannot be read back afterwards — a copy of the database is not a working provisioning credential.
  • The OIDC client secret of the application you register is stored as the sign-in library stores it: in the database, in cleartext, not under the envelope encryption that covers your upstream tokens. We would rather say so than imply otherwise. It is a credential for your tenant, it is rotatable from your provider, and disconnecting single sign-on deletes the row that holds it.

What an agent is allowed to do

Aggregation without control would mean one authorization buying an agent everything you have ever connected. Every tool on an enabled server has a mode, and you set it.

  • Allow is the default, and it is the absence of a rule rather than a rule — connecting a server does not become a configuration chore.
  • Deny refuses the call before any upstream contact happens. The tool is not called, and nothing about the attempt reaches the server.
  • Ask first holds the call while it waits for you. The agent waits about a minute before it is told to try again, and the request stays approvable for fifteen — so the usual case, where you notice after the agent has given up, still works: approve it and the retry goes straight through. Email and web push tell you one is waiting.

Omniio also watches the servers themselves. Upstream tools are fingerprinted, and if a server changes what a tool does or what it accepts after you have started using it, calls to that tool are held until you have seen the new definition and accepted it. A server that quietly rewrites a tool you already trusted does not get to inherit that trust.

Results coming back

Everything a tool returns lands in your agent’s context, and a model cannot reliably tell your request apart from a sentence that arrived inside a ticket, a calendar invite or a web page. That is prompt injection, and it does not attack Omniio — it attacks the agent, which is holding your credentials for every other server on the account.

Omniio is the one place that sees every result before the agent does, so it reads each one for content addressed to the agent rather than to you: instructions to disregard what came before, faked system turns, requests for the system prompt or for your keys, image beacons and other ways of sending data to an outside address, instructions to hide what it is doing from you, and invisible characters.

  • Nothing is blocked on a finding. An issue tracker legitimately contains a ticket that says “ignore all previous instructions” — somebody reported the attack — and a gateway that swallowed it would be breaking your tools to look secure. Only a tool policy you set refuses a call.
  • The agent is told instead. The result is handed over in full with a notice above it naming the server and stating that its content is data, not instruction. That is the defence that reaches the thing being attacked, in the same context, at the same moment.
  • It lands in the trail. The finding is recorded on the call, flagged in the activity log, and sent with the webhook — so “a server tried to steer my agent” is a question you can ask afterwards, and alert on.

What is recorded

Every run_tool invocation is written down: the server, the tool, the arguments sent, the result returned, whether it succeeded, how long the upstream took, and which client asked. It is a record you can read, filter and export, not telemetry we keep for ourselves.

  • Arguments and results are capped at 64 KB each, with the cut marked in place — a tool that returns multi-megabyte documents cannot quietly turn your audit trail into a storage bill.
  • Secrets and personal data are masked out before the row is written — card numbers that pass their check digit, IBANs that pass theirs, issued API keys and bearer tokens, private key blocks, signed tokens, email addresses, phone numbers and national ID numbers. The same masking runs on a held call before it is stored for approval, so nothing sits in clear text waiting for a decision or travels in the email that asks for one. Your agent receives the upstream’s reply in full and an approved call runs with its real arguments; the stored copy carries a marker naming what was taken out, and so does the export.
  • Entries are pruned on a daily schedule once they pass your plan’s retention window: 7 days on Free, up to 1 year on Enterprise. Pruning deletes rows; it does not archive them somewhere else.
  • Erasure of everything held about you is a request away rather than a self-serve button today: ask, and it is done within the month the GDPR allows — usually the same week.

Getting it out

That record is yours to take elsewhere: pushed to a URL you own as each call finishes, or pulled over HTTP on your own schedule. Both routes are credentialed, and the two credentials are stored differently because they are used differently.

  • Webhook signing secrets are sealed with AES-256-GCM, like every other secret here. Unlike an API key, this one has to be readable: Omniio needs the value itself to compute the HMAC on each delivery, so it is decrypted in memory for that signature and nothing writes it back. Rolling it issues a new secret and stops the old one immediately.
  • API keys are never stored at all. What is kept is a SHA-256 digest and the first ten characters, which is enough to tell two keys apart on screen and useless for authenticating. That is why the value is shown once, when it is created, and cannot be recovered afterwards — including by us.
  • Every delivery carries an HMAC-SHA256 over the timestamp and the body together, so a receiver can prove the request came from Omniio and reject a captured one replayed later. Deliveries are at-most-once and are not retried; the audit trail stays the durable copy.
  • Both are revocable on their own and neither can read anything the account’s own activity log cannot. Revoking a key keeps its row, so when it was switched off is on the record.

How to verify a delivery →

Limits, and what fails closed

An agent in a loop is the ordinary failure mode of this industry, and it is expensive in three directions at once — your bill, our database, and the upstream server’s own rate limits. Two ceilings sit in front of it.

  • A monthly allowance, counted per MCP call and reset at 00:00 UTC on the first of the month.
  • A burst ceiling per minute, from 60 / min on Free upward. It sits far above steady use of the same plan, so it catches runaways rather than work. A refused call is refused before it reaches an upstream server and is not billed.

Elsewhere, the rule is that a missing secret stops the feature rather than opening it. The scheduled jobs that prune and report usage require a bearer token and refuse every request when that token is not configured. Stripe webhooks are verified against the raw request bytes and a bad signature is answered with nothing useful.

Transport and browser headers

Everything is served over HTTPS. mcp.omniio.dev serves the MCP endpoint and nothing else — any other path on that host is redirected to the site, so there is no dashboard surface on the hostname your agents talk to.

These headers are on every response. They are read from the same module that configures them, so this list cannot describe a deployment that does not send them:

  • Strict-Transport-Security: max-age=63072000; includeSubDomains
    Two years of HTTPS-only, subdomains included, so the MCP host cannot be downgraded either.
  • X-Content-Type-Options: nosniff
    A response is the type it says it is; nothing stored is re-interpreted as script.
  • Referrer-Policy: strict-origin-when-cross-origin
    A link out of Omniio carries the origin and never a path like /app/activity.
  • Content-Security-Policy: frame-ancestors 'none'
    Nothing here may be framed, which is what stops a page being wrapped to harvest a consent click.
  • X-Frame-Options: DENY
    The same rule for clients older than the directive above.
  • Cross-Origin-Opener-Policy: same-origin-allow-popups
    A window we open cannot reach back into ours; popups still work, so clients that open the consent screen in one keep working.
  • Permissions-Policy: accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), browsing-topics=()
    Omniio asks for none of these, so an injected script cannot ask on our behalf.

Who else touches it

Omniio runs on other people’s infrastructure and says so. The full list, with what each one holds and where, is in the privacy policy; in short, hosting is Vercel, the database is Neon, payments are Stripe, and account email goes through a mail relay.

  • Card details never reach Omniio. Checkout is hosted by Stripe and we store its customer and subscription identifiers, not a card.
  • Semantic search over the tool catalogue is optional and, when it is on, embeds the catalogue’s tool names and descriptions — public metadata from the servers in the library. Your call arguments and results are never sent to an embedding provider.
  • Analytics tags load only after you accept them, and never on the signed-in dashboard.

Read the processor list in full →

What we don’t claim

The useful part of a page like this is the part that is not flattering. Omniio is operated by Marco Lorenzo Qureshi Grima, a sole trader established in Malta, trading as Omniio (a LimitBreakIT product). VAT Reg No 3297-8402, registered as exempt under article 11 of the Value Added Tax Act (Malta).

  • No SOC 2 or ISO 27001 report exists today. We have not been audited, and we will not imply otherwise with a badge. If your procurement process needs one, say so when you talk to us — it changes what we can sell you, and pretending otherwise wastes your time and ours.
  • Encryption at rest is not a claim about us. Sealing credentials protects them from a stolen database copy. The key is in the environment the service runs in, so the operator can decrypt what the service can decrypt. Anyone telling you otherwise about a hosted product is describing cryptography they have not implemented.
  • Data is not pinned to the EU yet. Some processors handle data outside the EEA under standard contractual clauses. EU-only residency is planned, not shipped, and it would be listed here as a fact if it were.
  • The REST API is separately rate limited. Its fixed per-key minute ceiling depends on the account plan; reads do not spend the MCP monthly allowance or burst budget. The current limits and response headers are published in the REST API reference.
  • Webhook deliveries are at-most-once. There is no retry queue. An endpoint that is down misses those events, and catching up means reading them back from the audit trail.
  • There is no bug bounty programme. Reports are read and answered by a person; they are not paid.
  • We do not train on your data and do not sell it. Results are returned to your agent as the upstream sent them, with one exception, and it is visible: a result that trips the injection scan is passed through in full with a notice added above it. Your arguments and results are stored for your audit trail and used for nothing else.

Report an issue

If you have found something, tell us before you tell anyone else and we will fix it. Reports go straight to the operator and are acknowledged within two working days.

Testing against your own account is welcome and we will not come after you for it. Please do not test against other people’s accounts, do not run load or denial-of-service tests against the endpoint, and do not use a finding to reach data that is not yours.
Or email hey@omniio.dev →