Octelium documentation · Latest
First Steps to Managing the Cluster
This guide walks through the main capabilities of an Octelium Cluster from a management perspective. It starts with a minimal Service and gradually introduces the different Service modes, secretless access, dynamic configuration and public access, then moves on to managing human and workload Users, including AI agents, and finally to controlling access with application-layer aware Policies. Every section links to the detailed management docs and guides where you can learn more.
An Octelium Cluster is managed similarly to a Kubernetes cluster. You describe your resources (e.g. Services, Users, Groups and Policies) declaratively in YAML files, and a single octeliumctl apply command synchronizes the Cluster state with them (read more here). Your entire Cluster configuration can therefore be stored in a Git repository where every change is reviewed, tracked and rolled back like any other code.
You can use Octelium Agent Skills with AI coding agents (e.g. Claude Code, Codex, etc...) to help you manage your Cluster. For example, you can ask your agent to create your first Users, Services, Groups and Policies, and it can prepare the YAML configuration and run the relevant octeliumctl commands for you. You can find the installation instructions here or via Ask AI → AI agent skills above.
Before You Begin
You need a running Cluster. If you do not have one yet, the quick installation guide here installs a single-node Cluster on any cheap cloud VM/VPS in a few minutes. You also need the Octelium CLIs on your own machine: octeliumctl manages the Cluster resources, while octelium connects to the Cluster and accesses its Services (read more here).
Since a Cluster is addressed by its domain, the commands and configurations in this guide use a placeholder for it. You can enter your own domain below to use it in all the examples:
Optional. Enter your Cluster domain to use it in place of <DOMAIN> in all the commands and configurations on this page. It is only stored in your browser.
Now install the CLIs and log in to the Cluster. Since the CLIs can work with multiple Clusters, it is easier to set the OCTELIUM_DOMAIN environment variable once instead of adding the --domain flag to every command:
The initial authentication token belongs to the root User that is created during the installation, and the Session it creates is allowed to access everything. Once you have defined your own User and an IdentityProvider as shown below, you can log in via your browser simply using octelium login and delete the initial Credential via octeliumctl delete cred root-init (read more about logging in here).
Applying Changes
Throughout this guide, we add resources to YAML files in a configuration directory (e.g. ~/octelium) and apply them all at once. You are free to split your resources across as many files and subdirectories as you want (read more here):
Here are the main resources that you will manage in this guide:
| Resource | Represents |
| Service | A protected resource (e.g. an API, a web app, an SSH server, a database, an LLM provider, an MCP server) implemented by an identity-aware proxy |
| Namespace | A group of Services that also acts as their parent domain |
| User | A HUMAN or WORKLOAD identity |
| Group | A set of Users that share the same Policies |
| Policy | A set of access control rules written in CEL or OPA |
| IdentityProvider | A GitHub, OpenID Connect or SAML 2.0 provider for web login, or an OIDC issuer trusted for workload assertions |
| Credential | An authentication token, OAuth2 client credential or access token issued for a User |
| Secret | A sensitive value (e.g. an upstream API key or password) referenced by other resources by its name |
| Session | The short-lived state through which an authenticated User interacts with the Cluster |
If you're familiar with Kubernetes, you might have noticed the resemblance between Octelium resources and Kubernetes resources. However, Octelium resources are neither Kubernetes resources nor CRDs. Octelium has its own gRPC-based API Server and its resources are stored in its PostgreSQL-based data store via the Resource Server. The same APIs are also available programmatically via the SDKs (read more here).
Services
Each protected resource is represented by a Service, which is implemented by an identity-aware proxy called Vigil. Every request to a Service is authenticated and authorized on a per-request basis before it is proxied to the upstream, and the upstream itself does not need to be aware of Octelium at all (read more about how Octelium works here).
Your First Service
We start with the simplest possible HTTP Service, named first-service, whose upstream is a public website. Add it to a file in your configuration directory (e.g. ~/octelium/services.yaml) as follows:
And apply it as follows:
Now connect to the Cluster. The octelium connect command connects your machine to the Cluster over WireGuard/QUIC tunnels and sets up a private DNS for its Services (read more here):
Once connected, you can access the Service by its name using any HTTP client:
octelium connect adds local.<DOMAIN>, which is the common private suffix of all the Cluster's Services, to your machine's DNS search domains, which is why the hostname first-service simply works. Every Service is also assigned stable private dual-stack addresses regardless of the addresses of its upstream (read more about Service DNS here).
You can also run octelium connect as an unprivileged process and map the Service to a localhost port instead (read more here):
Upstreams
The upstream does not have to be reachable from the Cluster itself. A Service can serve any of the following types of upstreams (read more here):
An upstream that is directly reachable from within the Cluster, such as a Kubernetes service in the same Kubernetes cluster, a resource in the same private cloud network, or a public SaaS API:
Modes and Secretless Access
Besides HTTP, Vigil understands several application-layer protocols. This application-layer awareness enables Policies to control access based on protocol-specific information (e.g. HTTP paths, SQL queries, LLM models, MCP tools), enables the access logs to record such information, and enables the Service to authenticate to the upstream on behalf of the User. The latter is called secretless access: the upstream credential (e.g. an API key, an SSH private key, a database password) is stored in the Cluster as a Secret and injected by the Service on-the-fly, so it is never shared with, distributed to or stored by any User (read more here).
Secrets are created via the octeliumctl create secret command, which reads the value from a prompt or a file, so that sensitive values are never stored along with your YAML configuration (read more here). Here are some examples:
The HTTP mode supports bearer, basic, custom header, OAuth2 client credentials and AWS SigV4 authentication to the upstream (read more here). Here is a Service for the GitHub API:
You can see more detailed examples for SaaS APIs, AWS S3 and AWS Lambda.
Embedded SSH and SOCKS5
The octelium client can itself serve SSH and SOCKS5 from the host it runs on, without an SSH server or a SOCKS5 proxy running on that host. Embedded SSH can even be served by an unprivileged octelium process. This is useful for fleets of servers, containers and IoT devices. The Cluster already includes the built-in essh.octelium and socks5.octelium Services for this purpose, so there is nothing to configure (read more about embedded SSH here and embedded SOCKS5 here):
You can also copy files from and to such hosts via the octelium cp command (read more here).
Dynamic Configuration
So far, all the requests to a Service use the same configuration. Dynamic configuration enables the Service to choose a different upstream, a different upstream account and credential, or different protocol-specific settings on a per-request basis depending on the identity and context of the request. Each rule has a condition that is written exactly like a Policy rule condition, the first matching rule chooses the named configuration, and the default config is used whenever no rule matches. A named configuration can also inherit from another one via parent (read more here):
Members of the ops Group log in as root, while every other authorized User logs in as the unprivileged ubuntu user:
Dynamic configuration is also supported by the other modes, including MYSQL, RDP, SOCKS5, LLM, MCP, DNS, TCP and UDP. Instead of choosing a named configuration, a rule can also generate the configuration on-the-fly via a CEL or OPA expression (read more here).
HTTP-Specific Features
Services that use the HTTP-based modes (i.e. HTTP, WEB, GRPC, LLM and MCP) also provide the capabilities of an API gateway such as header and path manipulation, request body validation, retries and CORS (read more here). Moreover, plugins are invoked conditionally on a per-request basis to provide rate limiting, caching, JSON schema validation, direct responses, Lua scripts and Envoy ExtProc (read more here). Here is an example:
LLM and MCP Gateways
The LLM and MCP modes are HTTP-based modes that understand the LLM inference APIs (i.e. OpenAI, Anthropic, Gemini and Bedrock) and the Model Context Protocol respectively. In addition to the HTTP capabilities above, they validate the requests, expose normalized protocol information (e.g. the LLM model and operation, or the MCP method and tool name) to Policies and dynamic configuration, provide protocol-aware plugins and emit protocol-aware access logs (read more about the LLM mode here and the MCP mode here):
An OpenAI gateway that is exposed publicly (see public access below) so that applications and AI agents can use it without the octelium client:
Any OpenAI-compatible client or SDK can now use https://llm.<DOMAIN>/v1 as its base URL and an Octelium access token (see workload Users below) instead of the OpenAI API key:
The upstream can also speak a different protocol than the one served to the clients. For example, OpenAI SDKs can use an Anthropic upstream via protocol translation (read more here).
You can see detailed examples of using Octelium as an AI gateway here and as an MCP gateway here.
Public and Anonymous Access
So far, Services are only accessible privately via octelium connect. HTTP-based Services can also be exposed publicly via the clientless BeyondCorp mode, where HUMAN Users access them via their browsers after logging in via the Cluster's web Portal, and WORKLOAD Users access them via standard OAuth2 client credentials or bearer access tokens without any client or SDK (read more here). Public Services are still authenticated and authorized on a per-request basis. You can also expose a Service for anonymous access, effectively using the Cluster as a hosting platform (read more here):
Authorized Users can now open https://grafana.<DOMAIN> in their browsers, including from phones and tablets. The WEB mode is the HTTP mode for web applications, for which the web Portal additionally shows a "Visit" button.
Namespaces
Every Service belongs to a Namespace, which groups Services by environment, team or project and acts as their parent domain. A Service that does not explicitly specify a Namespace belongs to the default Namespace (read more here). Here is a Namespace and a Service that belongs to it:
The Service is privately addressed as api.production (i.e. api.production.local.<DOMAIN>), and as api.production.<DOMAIN> if it is exposed publicly, which requires its own public DNS entry and TLS certificate (read more here). Policies attached to a Namespace apply to all of its Services as shown below.
Users and Identity
A User represents an identity that interacts with the Cluster and accesses its Services. There are two types of Users: HUMAN Users, and WORKLOAD Users for non-human entities such as servers, containers, CI/CD pipelines, applications and AI agents (read more here). Both types share the same identity model: they belong to Groups, are subjected to the same Policies, access the same Services via the same client-based and clientless modes, interact with the Cluster only through short-lived Sessions created upon a successful authentication (read more here), and are identified in the same access logs. They mainly differ in the way they authenticate:
| Authentication method | HUMAN | WORKLOAD |
| Web login via GitHub, OpenID Connect and SAML 2.0 IdentityProviders | ✓ | |
| Passkey login and MFA via FIDO2/WebAuthn, TOTP and TPM 2.0 Authenticators | ✓ | |
| Authentication token Credentials | ✓ | ✓ |
| OAuth2 client credentials | ✓ | |
| Access token Credentials used as bearer tokens | ✓ | |
| Secretless OIDC assertions (e.g. GitHub Actions, Kubernetes, Azure) | ✓ |
Human Users
HUMAN Users log in via the Cluster's web Portal using your identity providers. First, create an OAuth2/OIDC or a SAML 2.0 application in your identity provider and set its callback URL to https://<DOMAIN>/callback. For GitHub and OpenID Connect providers, store the application's client secret as a Secret:
Now define the IdentityProvider:
Create an OAuth app in your GitHub developer settings and set its homepage URL to https://<DOMAIN> (read more here):
Now define the User and add it to a Group (read more about Groups here). By default, the User's email is matched against the identity returned by any IdentityProvider. You can also bind explicit identities such as a GitHub username (read more here):
Once applied, linus can log in via the browser as follows:
HUMAN Users can also register FIDO2/WebAuthn (e.g. YubiKeys and platform passkeys), TOTP and TPM 2.0 Authenticators for MFA, re-authentication and passwordless passkey login (read more here). IdentityProviders can also set the authenticator assurance level (AAL) of every login from the provider's assertion and reject logins via post-authentication rules (read more here), which enables Policies to require, for example, a phishing-resistant AAL3 login to access production Services.
Workload Users
WORKLOAD Users are defined in the same way:
A WORKLOAD User can authenticate via any of the following methods, all of which result in a Session that is subjected to the same Policies:
Authentication tokens are used by the octelium and octeliumctl CLIs, typically from servers, containers and Kubernetes pods, where the workload can authenticate and connect in a single step (read more here):
You can read more about connecting from containers and Kubernetes.
AI Agents
AI agents are managed as WORKLOAD Users. They therefore share the same identity, authentication, access control and auditing model as your HUMAN Users and other workloads instead of requiring a separate system:
Identity without stored secrets An agent can authenticate via an OIDC assertion of the environment it runs in (e.g. its Kubernetes service account token or its GitHub Actions identity), as well as via OAuth2 client credentials, access tokens or authentication tokens.
Secretless access to models, tools and infrastructure The agent accesses LLM providers, MCP servers, SaaS APIs, databases and SSH servers via Services that inject the upstream credentials. Since the agent never holds a provider API key, a database password or an SSH private key, a manipulated agent (e.g. via prompt injection) cannot leak them, and revoking the agent's access is a matter of changing a Policy or disabling its User instead of rotating credentials.
Per-request, application-layer aware control Policies can restrict every agent to certain LLM models and operations, MCP tools and tool arguments, HTTP paths or SQL statements, while LLM plugins can enforce guardrails and token budgets per agent.
Attribution Every LLM request, MCP tool call and database query is logged along with the agent's User and Session.
Here is an example of an agent running as a pod in an EKS cluster that authenticates via its Kubernetes service account token:
The agent exchanges its service account token, projected with the https://<DOMAIN> audience, for an Octelium access token via the standard OAuth2 JWT assertion flow, and then uses the access token as the API key of its OpenAI and MCP clients for the llm and tickets-mcp Services shown above:
For agents that also need an isolated environment to run in, Cordium is an open source, self-hosted sandbox platform built on Octelium. Every Cordium Workspace has its own Octelium Session identity, so coding agents (e.g. Claude Code and Codex) and automated jobs running inside it access the authorized Services with standard tools such as curl, psql and ssh, while the upstream credentials never enter the sandbox (read more here).
Access Control
Octelium follows the zero trust security model: being authenticated or connected to the Cluster does not grant access to anything. Every request is denied unless a Policy explicitly allows it, and there is no built-in "admin" or "superuser" User (read more here). A Policy is a set of rules, each of which has an ALLOW or DENY effect and a condition written in CEL or OPA. For every request, Octovigil evaluates the Policies attached to the Session, the User, the User's Groups, the Service and its Namespace, as well as the global Policies. A matching DENY rule denies the request, otherwise a matching ALLOW rule allows it, otherwise the request is denied (read more here).
Granting Access
The simplest way to grant a User full access is to attach the allow-all Policy, which is created during the installation. This is typically how you define your own administrator User instead of using the initial root User:
For least privilege, you should instead grant access only where it is needed. Rules can be defined inline in any resource, or in standalone Policies that can be reused by any number of resources:
An inline Policy in a Service acts as a resource-based policy. Here, every User whose email belongs to example.com can access first-service:
Application-Layer Aware Policies
Since Vigil understands the protocol of every request, rules can use the request's content in order to grant access to certain operations instead of the entire resource (read more here):
The members of the dev Group can send GET requests to paths under /api/v1/, while the members of the finance Group can issue refunds of up to 500 according to the JSON request body (read more here):
Policies also support rule priorities that enable ALLOW rules to override DENY rules, enforcement rules, nested conditions, OPA Rego scripts and global Policies defined in the ClusterConfig (read more here). You can also attach Policies to Credentials in order to restrict the Sessions created by them (read more here). The CEL guide here shows you how to write more advanced conditions.
Visibility
Every request, whether allowed or denied, is recorded in an access log that identifies the User, Session, Device and Service, the Policy that decided the request, and the application-layer details of the request such as HTTP paths, SQL queries, SSH session recordings, LLM models and token usage, or MCP tool calls. Access logs are exported in real time to your OpenTelemetry OTLP receivers, from which they can be sent to your log management and SIEM tools (read more here).
What Now?
Run it in production Install a multi-node Cluster on top of any cloud-based or on-prem Kubernetes cluster here.
Connect and access Read more about connecting to Clusters here and accessing Services here.
Gateways Use Octelium as an API gateway, AI gateway, MCP gateway or a gateway for SaaS APIs.
Hosting and tunnels Use Octelium as an ngrok alternative, deploy Next.js/Vite apps, or use it as a Kubernetes ingress controller.
Homelab Access remote VSCode, self-host Pi-hole or set up SOCKS5 exit nodes.
Automation Manage the Cluster programmatically via the SDKs, and use Octelium from GitHub Actions and Helm.
Understand the design Read how Octelium works, how it implements zero trust, and how it compares to other access solutions.