Documentation
¶
Overview ¶
Package codex implements the Codex credential and agent provider.
The Codex provider manages OpenAI credentials for running the Codex CLI in containers. It implements both CredentialProvider and AgentProvider interfaces.
Authentication ¶
Codex supports OpenAI API key authentication:
- API keys are validated against the /v1/models endpoint
- Keys must start with "sk-" prefix
- The real API key is never exposed to containers
- Proxy injection adds Authorization headers at network layer
Credential Provider ¶
The credential provider configures:
- Proxy headers for api.openai.com with Bearer token
- Container env with OPENAI_API_KEY placeholder (passes format validation)
- No container mounts needed (uses staging directory approach)
Agent Provider ¶
The agent provider handles:
- Container preparation with staging directory
- CLI registration for `moat codex` commands
- Doctor diagnostics for Codex configuration
Generated Configuration ¶
PrepareContainer writes a ~/.codex/config.toml (see NewConfig) that adapts Codex to running inside a moat container:
- Approvals and Codex's own sandbox are off. The container is already the isolation boundary, and Codex's sandbox blocks the network the proxy provides. `moat codex --noyolo` restores Codex's own defaults.
- shell_environment_policy.inherit = "all", so commands Codex runs keep the proxy variables (HTTP_PROXY, SSL_CERT_FILE, ...). Codex's "core" default strips them.
- /workspace is marked trusted, skipping the first-run trust prompt.
- Remote (proxy-relay) and local MCP servers both go in [mcp_servers]. Codex reads MCP servers from config.toml only; it ignores .mcp.json.
Placeholder Tokens ¶
The container receives placeholder values that pass format validation:
OPENAI_API_KEY=sk-moat-proxy-injected-placeholder-...
This allows the Codex CLI to start without prompting for credentials, while the real API key is injected by the proxy at the network layer.
Index ¶
- Constants
- Variables
- func DefaultDependencies() []string
- func GetCredentialName() string
- func HasCredential() bool
- func NetworkHosts() []string
- func PopulateStagingDir(cred *provider.Credential, stagingDir string) error
- func RenderOpenAIShellEnv() string
- func ValidateVersion(version string) error
- func WriteCodexConfig(stagingDir string, cfg Config) error
- type AuthKind
- type Config
- type DoctorSection
- type Grant
- type MCPServer
- type ProjectConfig
- type Provider
- func (p *Provider) CanRefresh(cred *provider.Credential) bool
- func (p *Provider) Cleanup(string)
- func (p *Provider) ConfigureProxy(proxyConfig provider.ProxyConfigurer, cred *provider.Credential)
- func (p *Provider) ContainerEnv(*provider.Credential) []string
- func (p *Provider) ContainerMounts(*provider.Credential, string) ([]provider.MountConfig, string, error)
- func (p *Provider) CredentialGrant() string
- func (p *Provider) DefaultDependencies() []string
- func (p *Provider) Grant(ctx context.Context) (*provider.Credential, error)
- func (p *Provider) IdentifiesAs(agent string) bool
- func (p *Provider) ImpliedDependencies() []string
- func (p *Provider) JoinCommand(opts provider.JoinOpts) ([]string, error)
- func (p *Provider) Name() string
- func (p *Provider) NetworkHosts() []string
- func (p *Provider) PrepareContainer(ctx context.Context, opts provider.PrepareOpts) (*provider.ContainerConfig, error)
- func (p *Provider) Refresh(ctx context.Context, _ provider.ProxyConfigurer, cred *provider.Credential) (*provider.Credential, error)
- func (p *Provider) RefreshInterval() time.Duration
- func (p *Provider) RegisterCLI(root *cobra.Command)
- type ShellEnvironmentPolicy
- type SubscriptionAuth
Constants ¶
const ( ApprovalNever = "never" ApprovalOnRequest = "on-request" SandboxFullAccess = "danger-full-access" SandboxWorkspaceWrite = "workspace-write" )
Approval policies and sandbox modes accepted by the Codex CLI. See https://developers.openai.com/codex/config-reference.
const CodexInitMountPath = "/moat/codex-init"
CodexInitMountPath is where the staging directory is mounted in containers.
const OpenAIAPIKeyPlaceholder = "sk-moat-proxy-injected-placeholder-0000000000000000000000000000000000000000"
OpenAIAPIKeyPlaceholder is a placeholder that looks like a valid OpenAI API key. Some tools validate the API key format locally before making requests. Using a valid-looking placeholder bypasses these checks while still allowing the proxy to inject the real key at the network layer.
const OpenAIShellEnvFileName = "openai-env.sh"
const WorkspacePath = "/workspace"
WorkspacePath is where moat mounts the workspace inside the container.
The generated config pre-trusts this path. That serves two purposes, and the second is easy to miss: besides skipping the interactive trust prompt (Codex only loads project-scoped `.codex/` layers for trusted projects), it is what lets `codex exec` run in a workspace that is not a git repository. Without a trust entry, `codex exec` exits 1 with "Not inside a trusted directory and --skip-git-repo-check was not specified" before doing any work.
Variables ¶
var OpenAIShellEnvPath = path.Join("$HOME", ".codex", OpenAIShellEnvFileName)
Functions ¶
func DefaultDependencies ¶
func DefaultDependencies() []string
DefaultDependencies returns the default dependencies for running Codex CLI.
func GetCredentialName ¶ added in v0.3.1
func GetCredentialName() string
GetCredentialName returns the name under which the Codex credential is stored. Returns empty string if no credential exists.
func HasCredential ¶
func HasCredential() bool
func NetworkHosts ¶
func NetworkHosts() []string
NetworkHosts returns the list of hosts that Codex needs network access to. These should be added to the network allow list for containers running Codex.
func PopulateStagingDir ¶
func PopulateStagingDir(cred *provider.Credential, stagingDir string) error
PopulateStagingDir populates the Codex staging directory with auth configuration.
Files added:
- auth.json (synthetic subscription tokens or a placeholder API key), or nothing at all when no credential was granted
SECURITY: The real token is NEVER written to the container filesystem. Authentication is handled by the TLS-intercepting proxy at the network layer.
func RenderOpenAIShellEnv ¶ added in v0.8.0
func RenderOpenAIShellEnv() string
func ValidateVersion ¶ added in v0.8.0
ValidateVersion checks the Codex auth adapter's explicitly supported range.
func WriteCodexConfig ¶
WriteCodexConfig writes cfg as config.toml in the staging directory. moat-init copies it to ~/.codex/config.toml at container startup.
Types ¶
type AuthKind ¶ added in v0.8.0
type AuthKind int
AuthKind is the credential a run stages for Codex. Every auth-dependent decision in this package routes through it so staging, config, and the container's version gate cannot disagree about which mode is in effect.
const ( // AuthNone means no Codex credential was granted. Codex is left // unauthenticated so it prompts for login, rather than being handed a // fabricated subscription the proxy will never honor. AuthNone AuthKind = iota // AuthAPIKey is a stored OpenAI API key, injected on api.openai.com. AuthAPIKey // AuthSubscription is a stored ChatGPT subscription, injected as a scoped // credential bundle on the ChatGPT Codex backend. AuthSubscription )
func ResolveAuthKind ¶ added in v0.8.0
func ResolveAuthKind(cred *provider.Credential) AuthKind
ResolveAuthKind maps a resolved credential to the staging mode it implies. An unrecognized provider is treated as AuthNone: guessing wrong here writes an auth file that claims an authentication Codex does not actually have.
type Config ¶ added in v0.8.0
type Config struct {
// Both are omitempty: clearing a field must drop the key so Codex uses its
// own default. Emitting `chatgpt_base_url = ”` instead configures an empty
// origin, which is not the same thing.
CLIAuthCredentialsStore string `toml:"cli_auth_credentials_store,omitempty"`
ChatGPTBaseURL string `toml:"chatgpt_base_url,omitempty"`
ApprovalPolicy string `toml:"approval_policy"`
SandboxMode string `toml:"sandbox_mode"`
ShellEnvironmentPolicy ShellEnvironmentPolicy `toml:"shell_environment_policy"`
Projects map[string]ProjectConfig `toml:"projects,omitempty"`
MCPServers map[string]MCPServer `toml:"mcp_servers,omitempty"`
}
Config is the subset of ~/.codex/config.toml that moat generates.
Scalar keys must stay ahead of the table-valued ones: TOML assigns bare key/value pairs to the table they follow, so a scalar emitted after [shell_environment_policy] would land inside it.
func NewConfig ¶ added in v0.8.0
NewConfig returns the base Codex configuration for a moat container.
requireApproval selects between the two policies moat supports:
- false (default): approvals off and Codex's own sandbox off. The container is already the isolation boundary, and Codex's sandbox blocks the network the moat proxy provides, so nesting it breaks any command the agent runs that needs the network or writes outside the workspace.
- true (--noyolo): Codex's own defaults, so it prompts before acting and confines commands to the workspace with the network off.
The environment policy is always "all". Codex's "core" default keeps only PATH/HOME/SHELL/TMPDIR/LANG/LOGNAME/USER, which strips HTTP_PROXY, SSL_CERT_FILE, and NODE_EXTRA_CA_CERTS from every command Codex spawns - i.e. exactly the variables that route a container command through the moat proxy. moat curates the container environment already.
type DoctorSection ¶
type DoctorSection struct{}
DoctorSection implements doctor.Section for Codex configuration.
type MCPServer ¶
type MCPServer struct {
Command string `toml:"command,omitempty"`
Args []string `toml:"args,omitempty"`
Env map[string]string `toml:"env,omitempty"`
Cwd string `toml:"cwd,omitempty"`
URL string `toml:"url,omitempty"`
HTTPHeaders map[string]string `toml:"http_headers,omitempty"`
}
MCPServer is a single entry under [mcp_servers]. Command/Args/Env/Cwd describe a stdio server; URL/HTTPHeaders describe a streamable HTTP server. Codex reads MCP servers from config.toml only - it ignores the .mcp.json file other agents use.
type ProjectConfig ¶ added in v0.8.0
type ProjectConfig struct {
TrustLevel string `toml:"trust_level"`
}
ProjectConfig marks a project path trusted or untrusted.
type Provider ¶
type Provider struct{}
func (*Provider) CanRefresh ¶ added in v0.8.0
func (p *Provider) CanRefresh(cred *provider.Credential) bool
func (*Provider) ConfigureProxy ¶
func (p *Provider) ConfigureProxy(proxyConfig provider.ProxyConfigurer, cred *provider.Credential)
ConfigureProxy installs the real access token and account identity as one atomic bundle. Older proxy implementations do not receive a fallback static credential: failing closed is safer than widening a subscription token.
func (*Provider) ContainerEnv ¶
func (p *Provider) ContainerEnv(*provider.Credential) []string
Subscription auth is represented by synthetic auth.json, not an API-key env var. In particular OPENAI_API_KEY must not override Codex's selected mode.
func (*Provider) ContainerMounts ¶
func (p *Provider) ContainerMounts(*provider.Credential, string) ([]provider.MountConfig, string, error)
func (*Provider) CredentialGrant ¶ added in v0.8.0
CredentialGrant returns the logical Codex grant. The run resolver prefers subscription auth and falls back to an independently stored OpenAI API key.
func (*Provider) DefaultDependencies ¶ added in v0.8.0
func (*Provider) IdentifiesAs ¶ added in v0.8.0
IdentifiesAs reports whether a run with the given recorded Agent field was created by the codex provider. This serves only the pre-upgrade fallback path in `moat join` — runs created before joinable_agents existed. Runs created since are matched against their persisted capability set instead.
func (*Provider) ImpliedDependencies ¶
func (*Provider) JoinCommand ¶ added in v0.8.0
JoinCommand builds the in-container command for a joined codex session, mirroring the BuildCommand closure in runCodex (cli.go).
Approval policy and sandbox mode are deliberately absent: they live in the generated ~/.codex/config.toml so every launch path gets the same behavior, and `codex` and `codex exec` accept different flags.
func (*Provider) NetworkHosts ¶ added in v0.8.0
func (*Provider) PrepareContainer ¶
func (p *Provider) PrepareContainer(ctx context.Context, opts provider.PrepareOpts) (*provider.ContainerConfig, error)
PrepareContainer sets up staging directories and config files for Codex CLI. It creates a staging directory with auth.json and config.toml files that will be copied to ~/.codex at container startup by moat-init.
func (*Provider) Refresh ¶ added in v0.8.0
func (p *Provider) Refresh(ctx context.Context, _ provider.ProxyConfigurer, cred *provider.Credential) (*provider.Credential, error)
func (*Provider) RefreshInterval ¶ added in v0.8.0
func (*Provider) RegisterCLI ¶
RegisterCLI registers Codex-related CLI commands. This adds the `moat codex` command group with subcommands.
type ShellEnvironmentPolicy ¶ added in v0.8.0
type ShellEnvironmentPolicy struct {
Inherit string `toml:"inherit"`
}
ShellEnvironmentPolicy controls the environment Codex passes to the commands it runs.
type SubscriptionAuth ¶ added in v0.8.0
type SubscriptionAuth struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
AccountID string `json:"account_id"`
}
SubscriptionAuth is the encrypted payload stored for a Codex subscription. It is never staged into the container or sent over the daemon API.