codexexecgateway

package
v0.60.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 19, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package codexexecgateway: types are defined in execmodel to avoid an import cycle with the handlers sub-package. Type aliases here re-export them under the parent package name so existing code outside this package is unaffected.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrMalformed    = errors.New("malformed capability token")
	ErrBadSignature = errors.New("bad capability token signature")
	ErrExpired      = errors.New("capability token expired")
)

Functions

This section is empty.

Types

type CapPayload

type CapPayload struct {
	TurnID      string `json:"turn_id"`
	WorkspaceID string `json:"workspace_id"`
	IAT         int64  `json:"iat"`
	EXP         int64  `json:"exp"`
}

CapPayload is the parsed JSON payload from a CODEX_EXEC_GATEWAY_TOKEN.

Per the 2026-05-16 fixed-tools redesign, tokens are workspace-scoped: a single token authorises any exe_id bound to the named workspace. The /bridge handler verifies workspace ownership against the workspace_executors table at request time, replacing the prior exe_ids[] allow-list shipped in the payload.

func VerifyCapabilityToken

func VerifyCapabilityToken(token string, secret []byte) (CapPayload, error)

VerifyCapabilityToken parses and verifies a 3-part HMAC capability token.

Token format (spec § Capability token):

token   = base64url(header) "." base64url(payload) "." base64url(sig)
header  = '{"alg":"HS256","typ":"CXG"}'
payload = '{"turn_id":"...","workspace_id":"...","iat":...,"exp":...}'
sig     = HMAC-SHA256(secret, base64url(header) "." base64url(payload))

base64url encoding uses no padding (RFC 7515 / JWT convention). HMAC comparison is constant-time via hmac.Equal to prevent timing attacks. Expiry is checked in UTC.

type Config

type Config struct {
	Port                      string
	DatabaseURL               string
	CapTokenHMACSecret        []byte
	InternalSharedSecret      string
	AgentserverInternalSecret string
	// PublicWSBaseURL is the wss:// origin used in the response of the
	// upstream-compat `POST /cloud/executor/{exe_id}/register` endpoint.
	// Example: "wss://codex-exec.agent.cs.ac.cn:443". When empty, the
	// endpoint synthesises a URL from the incoming request's Host header
	// (less reliable behind proxies but useful in dev).
	PublicWSBaseURL string
	// PublicHTTPSBaseURL is the https:// origin the relay endpoint is
	// reachable at — embedded in CreateRelay responses so env-mcp can
	// build curl PUT/GET commands. Example:
	// "https://codex-exec.agent.cs.ac.cn". When empty, the relay
	// /api/exec-gateway/relay/create endpoint refuses to mint tickets
	// (env-mcp falls back to the ws cat-pump path).
	PublicHTTPSBaseURL string
	// RelayDefaultTTL caps how long a minted ticket waits for both
	// sides to connect before timing out. Defaults to 5 minutes.
	RelayDefaultTTL time.Duration
	// RelayMaxPerWorkspace caps concurrent relays per workspace.
	// Defaults to 16; protects gateway memory from runaway agents.
	RelayMaxPerWorkspace int
	LogLevel             slog.Level
}

WebSocket keepalive (ping interval + idle timeout) is phase-2; nhooyr's defaults govern for now.

func LoadConfigFromEnv

func LoadConfigFromEnv() (Config, error)

func (Config) Validate

func (cfg Config) Validate() error

Validate checks that security-critical fields are populated. NewServer calls this so that direct Config{} construction cannot silently bypass HMAC checks.

type ConnRegistry

type ConnRegistry struct {
	// contains filtered or unexported fields
}

ConnRegistry tracks the single live inbound /codex-exec/{exe_id} connection per exe_id. Each entry is wrapped in an *inboundConn that can host multiple concurrent /bridge sessions multiplexed by stream_id (per the 2026-05-17 redesign).

Re-registering an exe_id evicts the prior inboundConn; the caller MUST call close() on the evicted conn so its routes get fanned out and the underlying ws closes.

func NewConnRegistry

func NewConnRegistry() *ConnRegistry

func (*ConnRegistry) ConnectedIDs

func (r *ConnRegistry) ConnectedIDs() []string

ConnectedIDs returns a snapshot of currently registered exe_ids.

func (*ConnRegistry) Lookup

func (r *ConnRegistry) Lookup(exeID string) (*inboundConn, bool)

Lookup returns the registered inbound for exeID, if any.

func (*ConnRegistry) Register

func (r *ConnRegistry) Register(exeID string, ic *inboundConn) (evicted *inboundConn)

Register inserts ic for exeID. If a previous inboundConn was registered for the same exeID, returns it as evicted. The caller MUST close the evicted inbound; failing to do so leaks its reader goroutine and orphans its bridge sessions.

func (*ConnRegistry) Unregister

func (r *ConnRegistry) Unregister(exeID string, ic *inboundConn)

Unregister removes exeID only if its current value is ic. This guards against a goroutine for an old inbound deleting a freshly registered one after eviction.

type ConnectedExecutor

type ConnectedExecutor = execmodel.ConnectedExecutor

type Executor

type Executor = execmodel.Executor

Re-export aliases preserve the package's external API. *Executor and *execmodel.Executor are the same type — aliases are zero-cost.

type RevokedSet

type RevokedSet struct {
	// contains filtered or unexported fields
}

RevokedSet is a bounded, concurrent-safe set of revoked turn_ids with per-entry expiry. Designed for the spec's "in-memory revoked set, cap ~10k, periodically pruned of entries past their original exp".

RevokedSet is bounded; when at cap, Add evicts the oldest entry by insertion order, NOT by expiry. If the evicted entry's exp is still in the future, that turn_id is silently un-revoked (its token will pass Contains as if never revoked, until its own exp). Add returns a bool indicating this case so callers can log/alert.

func NewRevokedSet

func NewRevokedSet(cap int) *RevokedSet

func (*RevokedSet) Add

func (r *RevokedSet) Add(turnID string, exp int64) (evictedLive bool)

Add inserts (turnID, exp). Re-adding refreshes the entry's position. When at capacity, the oldest entry is evicted. Returns evictedLive=true if the evicted entry's exp was still in the future — callers should log a warning because that turn_id is no longer blocked by the revoked set.

func (*RevokedSet) Contains

func (r *RevokedSet) Contains(turnID string) bool

Contains reports whether turnID is in the set (regardless of expiry — callers may rely on Prune to clean stale entries).

func (*RevokedSet) Prune

func (r *RevokedSet) Prune()

Prune removes entries whose exp has passed.

func (*RevokedSet) Size

func (r *RevokedSet) Size() int

Size returns the current number of entries.

func (*RevokedSet) StartPruner

func (r *RevokedSet) StartPruner(stop <-chan struct{}, interval time.Duration)

StartPruner runs Prune at the given interval until stop is closed. Caller is responsible for closing stop on shutdown.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server bundles the chi router with its dependencies. Server wires the routes for codex-exec-gateway. Production must always be constructed with a real *Store; tests that exercise only auth-rejection paths may use newServerNoStoreForTesting.

func NewServer

func NewServer(cfg Config, store *Store) (*Server, error)

NewServer is the production constructor. Refuses a nil store so a misconfigured deploy can't silently bypass the /bridge ownership check (which falls back to "skip + warn" when store is nil for the sake of test wiring).

func (*Server) Routes

func (s *Server) Routes() http.Handler

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store provides Postgres access for executors + workspace bindings. The underlying *sql.DB is intentionally private: callers must use the declared business methods and cannot bypass them via Exec/Begin/etc.

func NewStore

func NewStore(databaseURL string) (*Store, error)

NewStore opens a database connection and runs migrations.

func (*Store) BindWorkspaceExecutor

func (s *Store) BindWorkspaceExecutor(ctx context.Context, workspaceID, exeID, name, description string, isDefault bool) error

BindWorkspaceExecutor inserts a workspace ↔ executor binding (or upserts name/description/is_default on conflict). The name must be unique per workspace (enforced by uniq_workspace_executors_name); callers should treat a unique-violation as a user-input error and surface "name already taken in this workspace".

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying DB.

func (*Store) ConnectedExecutorsForWorkspace

func (s *Store) ConnectedExecutorsForWorkspace(ctx context.Context, workspaceID string, connectedIDs []string) ([]ConnectedExecutor, error)

ConnectedExecutorsForWorkspace returns the intersection of (workspace's bound executors) ∩ (the connected exe_id list passed in). Used by the internal `/api/exec-gateway/connected` endpoint.

func (*Store) CreateExecutor

func (s *Store) CreateExecutor(ctx context.Context, e Executor, registrationTokenHash string) error

CreateExecutor inserts a new executor row. Caller supplies the bcrypt hash.

func (*Store) DeleteExecutor added in v0.54.2

func (s *Store) DeleteExecutor(ctx context.Context, exeID string) error

DeleteExecutor removes the executor row and (via ON DELETE CASCADE) any of its workspace_executors bindings. Used by the orphan-cleanup path in agentserver's Register handler when Bind fails after Register. Idempotent: deleting an absent exe_id is a no-op.

func (*Store) GetExecutor

func (s *Store) GetExecutor(ctx context.Context, exeID string) (*Executor, error)

GetExecutor returns the executor by id, or (nil, nil) if absent.

func (*Store) GetRegistrationTokenHash

func (s *Store) GetRegistrationTokenHash(ctx context.Context, exeID string) (string, error)

GetRegistrationTokenHash returns the bcrypt hash used to authenticate /codex-exec/{exe_id} ws connections.

func (*Store) ListWorkspaceExecutors

func (s *Store) ListWorkspaceExecutors(ctx context.Context, workspaceID string) ([]ConnectedExecutor, error)

ListWorkspaceExecutors returns all bindings for a workspace. Per v0.54.0, name + description come from the workspace_executors row (binding-scoped), not the executor row.

func (*Store) OwnsExecutor added in v0.51.0

func (s *Store) OwnsExecutor(ctx context.Context, workspaceID, exeID string) (bool, error)

OwnsExecutor reports whether exeID is bound to workspaceID in the workspace_executors table. Used by /bridge to enforce workspace boundary on workspace-scoped cap tokens.

func (*Store) UnbindWorkspaceExecutor

func (s *Store) UnbindWorkspaceExecutor(ctx context.Context, workspaceID, exeID string) error

UnbindWorkspaceExecutor removes a binding row.

func (*Store) UpdateLastSeen

func (s *Store) UpdateLastSeen(ctx context.Context, exeID string) error

UpdateLastSeen sets the last_seen_at timestamp to NOW().

type WorkspaceExecutor

type WorkspaceExecutor = execmodel.WorkspaceExecutor

Directories

Path Synopsis
Package execmodel holds shared DTOs that cross the codexexecgateway↔handlers package boundary.
Package execmodel holds shared DTOs that cross the codexexecgateway↔handlers package boundary.
Package handlers contains HTTP handler functions for the codex-exec gateway.
Package handlers contains HTTP handler functions for the codex-exec gateway.
Package relay implements an in-memory ticket-based HTTPS byte relay for codex-exec-gateway.
Package relay implements an in-memory ticket-based HTTPS byte relay for codex-exec-gateway.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL