Skip to content

Security ​

Security properties and controls for AIMO: where secrets live, how identity is proved, what crosses your network boundary, and what the platform does not do. Setup steps are in Getting started, Architecture, Registration, Agent CLI and Operations.

Network boundary: no cloud path to your database ​

AIMO's cloud coordinates the product and talks to the agent over an outbound connection from your environment. There is no supported path from AIMO to your databases: no firewall rules, reverse tunnel, or SQL port opened for the cloud. If policy allows nothing outbound at all, the whole platform can be deployed on-prem with an LLM you supply; the boundary described below then sits entirely inside your network.

  • Outbound-only agent. The agent initiates TLS toward AIMO, through your forward proxy if you have one (Agent CLI). You do not publish databases or the agent to the public internet for normal operation.
  • Jobs, not shells. Work dispatched to the agent is limited to a fixed set of job types (analysis, monitor calculation, validation, connection tests). The agent is not general-purpose remote execution for arbitrary cloud code. See Operations.

Credential use and query execution stay on infrastructure you control, beside your data.

Data leaving the perimeter: aggregates and metadata, not raw rows ​

Raw field values are not bulk-exported to AIMO for routine monitoring. What crosses the boundary is summarized: schema and profiling metadata, aggregates over time blocks (counts, monitor values), and similar bounded payloads. Charts and the detector use those aggregates, not row-level dumps. Even the custom monitor family returns a single aggregate per block, inside the same fixed query shell as the other monitors. See Monitors.

Outlier detection runs on monitor time series in AIMO after the agent has produced the aggregates, not on a copy of your raw tables. See Outlier detection.

Human users: sign-in to the web UI ​

Access uses passkeys (WebAuthn) only; password login is not available. After sign-in, AIMO uses a server-side session; API calls from the UI are authorized from that session, with account roles: an admin can change account state and start work, a viewer is strictly read-only.

Sign-in is provided by the AIMO deployment as delivered. Bring-your-own IdP (for example OpenID Connect) is not available today; it is planned for a future release.

Recovery of a lost passkey goes through a one-time code sent to the account's verified email, after which you enroll a new passkey. Register a second passkey while you still have access anyway, and use platform passkey sync where your devices support it.

Agent identity: registering the keypair ​

Each agent uses an asymmetric keypair. The CLI generates an Ed25519 keypair at registration. Registration binds that keypair to your account: AIMO stores the public key with an expiry (365 days by default); the private key stays in your environment (unless you only supply the public key yourself). A short-lived, single-use registration token lets the CLI upload the public key once; the token is consumed as soon as the public key is accepted. See Registration. That flow is pairing only, not day-to-day authentication.

Agent JWTs: authenticating with the stored keypair ​

The running agent does not use a static API password. It keeps the private key (and related env, e.g. the agent UUID) on disk or in the container. To obtain a token it signs a timestamped request with the private key; AIMO verifies the signature against the stored public key, checks that the timestamp is within five minutes, and issues a JWT valid for six hours by default. The agent reuses that token for REST calls and the job channel and signs a fresh request shortly before it expires, so a captured request cannot be replayed later and no long-lived secret ever travels. Once the stored public key's expiry lapses, the token endpoint refuses to issue tokens until the key is renewed via key update or re-registration.

The token endpoint applies a per-IP limit of 10 requests per minute by default; overloaded clients see HTTP 429.

Agent WebSocket channel ​

The job channel requires a valid agent JWT before it carries work. Unauthenticated or invalid connections are closed. A connection from an agent that speaks a different protocol version than the server is closed too, with a message naming the version the server requires; pulling the current image resolves it.

Database credentials: encryption, storage, decryption ​

Connection credentials are never typed into the browser for storage. They are created through the agent CLI, encrypted before upload, and stored as ciphertext in AIMO's database.

Database credentials: encrypted in your environment, stored as ciphertext, decrypted only back on the agent Six steps in a loop across a boundary. In your environment: the agent CLI takes the database credentials; it encrypts them with a passphrase that never leaves your environment. Across the boundary, AIMO stores only the ciphertext with the connection's name and type. When a job runs, AIMO hands the ciphertext back with the job; the agent decrypts it on your side; and queries your database with a read-only role. A line at the foot says AIMO never holds the passphrase, so the stored ciphertext is not a usable password. YOUR ENVIRONMENT AIMO 01 Agent CLI: add Typed once, in your own shell, never in a browser. 02 Encrypted locally A key from your passphrase (scrypt), AES-256-GCM. 03 Stored as ciphertext With the name and the type. No key, no passphrase. 04 Sent back with a job Each job carries the ciphertext it needs. 05 Decrypted on the agent With the passphrase that never left your side. 06 Your database, queried Through a read-only role, for aggregates only. AIMO never holds the passphrase, so the stored ciphertext is not a usable password.
Credentials are encrypted on the agent with a passphrase AIMO never sees, stored as ciphertext in the cloud, and decrypted only back on the agent when a job runs. The plaintext password never reaches AIMO.

Follow the loop in the diagram: you enter credentials in the agent CLI, they are encrypted locally with AIMO_AGENT_PASSPHRASE, and only the ciphertext is uploaded. Encryption derives a 256-bit key from the passphrase with scrypt (N 16384, r 8, p 1, a random 16-byte salt) and encrypts the credentials with AES-256-GCM under a random 12-byte nonce; the stored blob carries only a version, the KDF name and parameters, the salt, the nonce and the ciphertext, no key material. If AIMO_AGENT_PASSPHRASE is unset the agent derives the key from its private key instead, which is why register writes a passphrase into aimo_agent.sh for you to replace. AIMO keeps that blob (plus a name and type) at rest. When work needs your database, AIMO hands the ciphertext back with the job, and the agent decrypts it on your side to run the query. Because the passphrase stays with you, the stored ciphertext is never a usable plaintext password in the cloud.

  • At rest. AIMO stores only the ciphertext (plus metadata such as name and type). Decryption for database access happens on the agent when executing jobs, using the passphrase available there.
  • API responses. List and get APIs may return ciphertext to authorized clients. Without agent-side key material, that data is not usable as plaintext passwords.
  • Credentials kept on the agent. A connection given to the agent at start (start --connection-... or the AIMO_AGENT_CONNECTION__* variables, see Agent CLI) is stored in AIMO as a name, a type and a reference to the agent, with no URL, password or ciphertext. Jobs carry the reference, and the agent fills in the credentials it was started with.

The CLI's show command prints non-secret fields for debugging; sensitive fields are masked. Agent REST operations that mutate connections require a valid agent JWT.

Read-only database access ​

The credentials you configure should belong to a read-only database role. The agent only ever reads: schema reflection, bounded sampling, monitor aggregates. A read-only role is the authoritative guarantee that nothing AIMO runs can write to or drop data from your tables. On top of the role, every client-database connection runs in autocommit, and where the engine offers a session-level read-only mode the agent switches it on (PostgreSQL's default_transaction_read_only, MySQL's SET SESSION TRANSACTION READ ONLY, SQLite's query_only, DuckDB's read-only open of a database file; a DuckDB connection given as a URL opens as the URL says). The other engines rely on the role alone.

This matters most for the free-form SQL slots: the custom family's aggregate, raw conditions, and expression fields. The read-only role is the authoritative protection; on top of it, the platform applies defense in depth before any fragment reaches your database. It parses each fragment to a syntax tree (dialect-agnostic) at the compile chokepoint, the same validation running wherever a spec is deserialized, and accepts only a single read-only expression:

  • One statement only. Multiple statements (anything separated by ;) are rejected.
  • No structural escapes. Subqueries, CTEs, set operations, window functions, and any DML or DDL (INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, MERGE, …) are rejected as node types in the parse tree, regardless of spelling.
  • No SQL comments, so nothing can comment out the surrounding query.
  • No dangerous functions. A deny set covering sleeps and other denial-of-service primitives, filesystem reads, network and cross-database egress, and OS execution (pg_sleep, benchmark, generate_series, pg_read_file, dblink, xp_cmdshell, …) is rejected.

Because checks run on the parsed tree, a keyword that appears as a string literal or quoted identifier (e.g. WHEN status = 'DELETED') is harmless and accepted. See Monitors.

Job payloads: typed, bounded work only ​

Work must deserialize into known job models. Unknown or malformed payloads are rejected. Each job carries structured fields (credential references, table IDs, time blocks, monitor definitions), not executable scripts. That aligns with the strict monitor type system: SQL on your database is generated from validated monitor definitions, not unconstrained strings in the envelope. See Operations and Monitors.

Operational hygiene ​

  • aimo_agent.sh and private keys. Treat as secrets: restrict filesystem permissions, avoid committing to version control, rotate via key update or re-registration when policy requires.
  • Registration tokens. Treat like one-time passwords: valid for one hour, shown only at creation.
  • TLS. Use HTTPS and WSS between agents and AIMO in production on untrusted networks; the published images are preset to it.

For diagrams, see Architecture.