Octelium is a free and open source, self-hosted zero trust access platform. Its database modes authenticate to the upstream on the User's behalf, so nobody receives a password or a connection string, the upstream account is forced by configuration, and every request is authorized and recorded.
Unified identity management for humans, workloads and AI agents
Adaptive authentication for humans and non-human identities
Identity-based, application-layer-aware access control
OpenTelemetry-native real-time visibility and auditing
Unified access architecture for private and SaaS resources
Every request from a human, a browser, a CLI, a workload or an AI agent is authenticated against your identity providers, authorized per request at the application layer by the Octelium Service in front of the resource, exported to your SIEM and observability stack whether it is allowed or denied, and only then routed to the upstream HTTP API, database, SSH server, Kubernetes cluster, LLM or MCP server.
The platform
A unified secure access platform
Databases are one mode of the same platform. Each capability is described in detail below, in the same order.
Each database is represented by a Service, which is served by an identity-aware proxy that abstracts away the network details of the upstream behind it. People keep using psql, mysql and their usual GUI clients.
A client running on this device is asking to log in to acme.com. Approve it only if you have just started a login yourself.
Approve
Reject
Sign in with a passkey
acme.com
J
jane@acme.com
Passkey
Touch the sensor to continueVerifying…Verified
Cancel
~
~❯
New Tab
Search or type a URL
grafana
argocd
jenkins
kibana
Signing in once, then reaching Services with and without a client
A browser opens grafana.acme.com and is redirected to the Cluster sign-in page at acme.com/login.
jane signs in with a passkey verified by a biometric on the device, and the browser returns to the Grafana dashboard at grafana.acme.com with no client installed.
In a terminal, octelium connect -d opens the browser at acme.com/callback/success/approval, where the same browser session approves the login with one click.
~ ❯ octelium connect -d
Please authenticate yourself using Octelium web Portal
Waiting for you to approve the login in your browser. Press Ctrl-C to cancel.
You are now authenticated as jane
Octelium has started running in detached mode.
~ ❯ curl -s my-api/v1/users/42 | jq
{
"id": 42,
"name": "Jane Doe",
"email": "jane@acme.com"
}
~ ❯ psql -h pg-prod -d shop
psql (17.4)
Type "help" for help.
shop=> SELECT * FROM orders LIMIT 3;
id | status | total
-----+---------+--------
981 | paid | 129.00
982 | shipped | 48.50
983 | paid | 310.25
(3 rows)
shop=> \q
~ ❯ ssh bastion
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-1021-aws x86_64)
Access control
Layer-7 aware access control with policy-as-code
Access is authorized using CEL or Open Policy Agent, against the identity behind the connection and the database request itself rather than the network path it arrived on.
jane, a member of the finance Group, runs curl -i billing/invoices/42 and gets HTTP/1.1 200 OK Content-Type: application/json {"id":42,"status":"paid","total":1290}. The access log records ALLOWED GET /invoices/42 with POLICY_MATCH.
jane, a member of the finance Group, runs curl -i -X DELETE billing/invoices/42 and gets HTTP/1.1 403 Forbidden X-Octelium-Unauthorized: true Content-Length: 0. The access log records DENIED DELETE /invoices/42 with NO_POLICY_MATCH.
LLM: the LLM Service openai is configured as follows.
kind: Service
metadata:
name: openai
spec:
mode: LLM
config:
authorization:
inlinePolicies:
- spec:
rules:
- effect: ALLOW
condition:
any:
of:
- match: '"ai-admins" in ctx.user.spec.groups'
- all:
of:
- match: '"ai-agents" in ctx.user.spec.groups'
- match: ctx.request.llm.operation == "GENERATE"
- match: ctx.request.llm.model == "gpt-5-mini"
support-bot, a member of the ai-agents Group, runs curl openai/v1/responses --json '{"model":"gpt-5-mini","input":"Hello"}' and gets { "id": "resp_68e8e1f0c4d0", "object": "response", "created_at": 1791628935, "status": "completed", "model": "gpt-5-mini-2025-08-07",. The access log records ALLOWED RESPONSES gpt-5-mini with POLICY_MATCH.
support-bot, a member of the ai-agents Group, runs curl openai/v1/responses --json '{"model":"gpt-5","input":"Hello"}' and gets {"error":{"message":"Octelium: unauthorized request","type":"permission_error","param":null,"code":"octelium_unauthorized"}}. The access log records DENIED RESPONSES gpt-5 with NO_POLICY_MATCH.
Kubernetes: the KUBERNETES Service k8s-prod is configured as follows.
kind: Service
metadata:
name: k8s-prod
spec:
mode: KUBERNETES
config:
authorization:
inlinePolicies:
- spec:
rules:
- effect: ALLOW
condition:
all:
of:
- match: '"sre-agents" in ctx.user.spec.groups'
- match: ctx.request.kubernetes.verb in ["get", "list"]
ops-agent, a member of the sre-agents Group, runs kubectl get configmaps -n production and gets NAME DATA AGE app-config 4 12d feature-flags 2 3d kube-root-ca.crt 1 12d. The access log records ALLOWED list configmaps · production with POLICY_MATCH.
ops-agent, a member of the sre-agents Group, runs kubectl get secrets -n production and gets Error from server (Forbidden): Octelium: Unauthorized request. The access log records DENIED list secrets · production with NO_POLICY_MATCH.
Secretless access
Passwords that are never distributed
The upstream password is stored in the Cluster as a Secret and used by the proxy to authenticate to the database once a connection has been authorized. Nobody receives it, so there is no connection string to rotate across laptops and scripts when someone leaves.
Upstream credentials injected per request by identity in the HTTP, SSH and PostgreSQL modes
HTTP: the HTTP Service stripe proxies https://api.stripe.com and picks the upstream credential of every request with dynamic configuration rules.
elena, a member of the finance-eu Group, runs curl -s stripe/v1/account | jq '{id, country}' without holding any credential. The rule "finance-eu" in ctx.user.spec.groups selects the eu config, so Octelium injects the Secret stripe-eu and the upstream authenticates the request as acct_1Pq8EuGmbH2bXvK3. The terminal shows { "id": "acct_1Pq8EuGmbH2bXvK3", "country": "DE" }
pay-agent, a member of the finance-us Group, runs curl -s stripe/v1/account | jq '{id, country}' without holding any credential. The rule "finance-us" in ctx.user.spec.groups selects the us config, so Octelium injects the Secret stripe-us and the upstream authenticates the request as acct_1Nf2UsQ7cLm4RtW9. The terminal shows { "id": "acct_1Nf2UsQ7cLm4RtW9", "country": "US" }
SSH: the SSH Service web-01 proxies ssh://10.0.4.21 and picks the upstream credential of every request with dynamic configuration rules.
sam, a member of the sre Group, runs ssh web-01 without holding any credential. The rule "sre" in ctx.user.spec.groups selects the root config, so Octelium injects the Secret root-key and the upstream authenticates the request as root. The terminal shows Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.14.0-1015-aws x86_64) root@web-01:~# id uid=0(root) gid=0(root) groups=0(root)
noah, a member of the developers Group, runs ssh web-01 without holding any credential. The rule "developers" in ctx.user.spec.groups selects the deploy config, so Octelium injects the Secret deploy-key and the upstream authenticates the request as deploy. The terminal shows Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.14.0-1015-aws x86_64) deploy@web-01:~$ id uid=1001(deploy) gid=1001(deploy) groups=1001(deploy)
PostgreSQL: the POSTGRES Service orders-db proxies postgres://10.0.2.8:5432 and picks the upstream credential of every request with dynamic configuration rules.
priya, a member of the backend Group, runs psql -h orders-db orders without holding any credential. The rule "backend" in ctx.user.spec.groups selects the rw config, so Octelium injects the Secret pg-rw and the upstream authenticates the request as app_rw. The terminal shows psql (17.2) Type "help" for help. orders=> UPDATE orders SET status = 'shipped' WHERE id = 42; UPDATE 1
bi-agent, a member of the analytics Group, runs psql -h orders-db orders without holding any credential. The rule "analytics" in ctx.user.spec.groups selects the ro config, so Octelium injects the Secret pg-ro and the upstream authenticates the request as app_ro. The terminal shows psql (17.2) Type "help" for help. orders=> UPDATE orders SET status = 'shipped' WHERE id = 42; ERROR: permission denied for table orders
Identity
One identity model for humans, workloads and AI agents
Human and workload Users share the same identity management, authentication, access control and visibility model. A migration job, an ETL run and an AI agent reaching a database are all identities rather than copies of a connection string.
Every request produces an AccessLog identifying the User, Session and Device alongside the application-layer detail of the database request, emitted in real time to your OpenTelemetry OTLP receivers.
The same stream covers HTTP requests, SSH session recordings, Kubernetes verbs and MCP and LLM operations, so database access is not audited in a separate system from everything else.
Export structured AccessLogs in real time over OpenTelemetry OTLP to the observability and SIEM systems you already operate.
Self-hosted, Kubernetes-native and fully programmable
Run the Cluster on Kubernetes infrastructure you control, scale the data plane horizontally, manage resources declaratively, automate the platform through APIs and SDKs, and optionally deploy containerized Service upstreams with the Cluster itself.
Cluster resources applied declaratively from YAML with octeliumctl and managed programmatically with the Go, TypeScript, Python and Rust SDKs
GitOps: cluster.yaml in the main branch of the ~/octelium repository describes the desired state of the Cluster. Its diff reads as follows, with + for added, ~ for changed and - for removed lines.
+ kind: Group
+ metadata:
+ name: payments
+ spec: {}
+ ---
+ kind: User
+ metadata:
+ name: alice
+ spec:
+ type: HUMAN
+ email: alice@acme.com
+ groups: ["payments"]
---
kind: User
metadata:
name: bob
spec:
type: HUMAN
~ groups: ["payments"]
---
kind: Service
metadata:
name: billing
spec:
mode: HTTP
config:
upstream:
~ url: http://10.0.3.24:8080
- ---
- kind: Service
- metadata:
- name: billing-legacy
- spec: ⋯
- ---
- kind: User
- metadata:
- name: john
- spec: ⋯
~/octelium ❯ octeliumctl apply --prune .
Group: payments Created
User: alice Created
User: bob Updated
Service: billing.default Updated
Service: billing-legacy.default Deleted
User: john Deleted
Cluster Core resources successfully applied
2 resources created
2 resources updated
2 resources deleted
Go: main.go uses the octelium-go SDK to list the Users of the Cluster and update the email and Policies of linus.
What database access has to solve, and what solves each part
Connecting to a database is easy. These are the problems that appear once more than a handful of people and jobs need to, next to the mechanism in an Octelium Cluster that handles each one.
01
Getting the password to the right people
Nothing is distributed. The upstream password is stored in the Cluster as a Secret and used by the identity-aware proxy to authenticate to the database once a connection has been authorized.
Revoking access is a Policy change or a Session revocation rather than rotating a password on the database and then chasing every laptop, script and connection string that held a copy of it.
The Service forces the upstream database user and database regardless of what the client asks for, so a shared account stops being a gap in the audit trail: each connection is still attributed to the Octelium User behind it.
Dynamic configuration maps groups to different upstream accounts, databases and credentials on the same Service, so an analyst lands on a read-only account and a database administrator on a writing one.
A Service upstream can be an address that is only reachable from the side of a connected host, so a database in another private network, a branch office or a customer environment needs no inbound port.
Protected public databases are covered by the same model, with TLS to the upstream forced by configuration, which brings managed providers under the same identity and audit story as internal ones.
There is nothing to change. People use psql, mysql and ordinary drivers against a stable private name, and applications use their normal connection libraries.
Every request produces an identity-aware AccessLog with the application-layer detail of the database request, streamed in real time over OpenTelemetry to your own log management and SIEM providers.
A password vault shortens how long a credential lives in one place. It still hands the credential to whoever asks, and the database still sees one account. Injecting it at the proxy changes both.
On every Octelium Cluster
The same platform capabilities ship with every deployment, whichever way you use it.
PostgreSQL and MySQL have dedicated application-layer modes, which give per-request authorization, secretless upstream credentials, dynamic configuration and protocol-aware auditing. That covers wire-compatible databases and managed providers as well. Anything else that speaks a TCP protocol can be reached through the generic TCP mode, including databases protected by mutual TLS, though authorization there happens at connection time.
How do people connect?
With their normal client. The Service is reached at a stable private name over the client-based tunnel, and the proxy authenticates to the database on the User's behalf. Nothing about psql, mysql, a GUI client or an application driver changes, and no password is entered. For MySQL Services, authorized Users currently connect with an empty password value.
Can different people get different privileges on the same database?
Yes. The Service forces an upstream database user and database regardless of what the client asks for, and dynamic configuration selects which upstream, account and credential are used per request from identity and context. One Service can therefore map an analyst group to a read-only account and a database administrator group to a writing one.
What about hosted databases?
They are covered by the same model. The upstream is simply a public address, TLS to the upstream can be required with the sslMode field, and the provider password stays in the Cluster as a Secret. That brings managed and serverless providers under the same identity, policy and audit story as internal databases.
Is the traffic recorded?
Yes. Each request produces an identity-aware AccessLog with the application-layer detail of the database request, streamed in real time over OpenTelemetry to your own log management and SIEM providers, attributed to the Octelium User, Session and Device behind it rather than to a shared database account.
Is Octelium free and open source?
Yes. Octelium is designed for self-hosting and does not require a proprietary hosted control plane. The core platform is free and open source. An optional enterprise package adds capabilities for organizations with additional operational, identity-management and security requirements, including the web console, SCIM provisioning and Secret encryption at rest.