A Kickside module that turns the official Codex CLI sign-in on a host into a
native Wippy LLM provider. Agents, model classes, and anything else that speaks
wippy.llm:generator can then run turns on ChatGPT-backed Codex models without
an OpenAI API key.
It ships:
- a
llm.providerregistry entry and thecontract.bindingthat implementswippy.llm:generatorandwippy.llm:provider; - a Responses transport for
https://chatgpt.com/backend-api/codexwith the session, thread, window, and turn headers the backend expects, zstd request compression, and bounded retries; - continuation metadata so encrypted reasoning items survive across tool steps;
- a service that mirrors the Codex CLI's own model cache into
llm.modelregistry entries and retires the ones the service stops listing; - an authenticated status page showing the sign-in state, cache freshness, and mirrored models.
Authentication is established by the official Codex login flow, not by this
module. codex login writes a ChatGPT credential cache into the Codex home
directory (~/.codex by default): auth.json, installation_id, and
models_cache.json.
The module reads that cache, refreshes the access token against
https://auth.openai.com/oauth/token when it is within two minutes of
expiring, and writes the rotated tokens back so the CLI and Kickside stay on
one credential. Nothing about the credential reaches registry data, logs,
prompts, HTTP responses, or generation results.
Two consequences follow, and both are deliberate:
- the module only works on a host where a person has run
codex login, and it inherits that person's ChatGPT entitlements; - the deployment, not the module, owns the path to that directory. It is
supplied as the mandatory
butschster.llm_codex:codex_home_pathrequirement, because no default this module could invent would be true.
Publish the module (see below), then install it from Kickside's System → Hub page. The host infers application-owned requirements and asks for the deployment-specific ones — here, the absolute Codex home path.
Set it to the Codex home of the account the deployment should run as, for
example /home/butschster/.codex. The runtime resolves no ~ and no
environment variables in a filesystem entry, so the value must be an absolute
path.
Verify from the Kickside shell: the Codex LLM Provider page reports the
sign-in state and lists the mirrored models. Sync models re-applies the
mirror immediately instead of waiting for the five-minute service tick.
Once installed and signed in, the mirrored model cards resolve like any other
llm.model:
local llm = require("llm")
local response = llm.generate({
model = "gpt-5.6-sol",
messages = { { role = "user", content = "Explain this stack trace." } },
})Model cards mirrored from the cache carry the classes coding, reasoning,
vision where the model accepts images, and one size class derived from the
service's own ranking (premium, balanced, or fast). A DB-backed model
card assigned by an admin still wins over the mirrored one.
Per-turn options the driver understands beyond the universal contract:
thinking_effort— reasoning effort, when the model card marks the model as a reasoning model;reasoning_summary— summary mode,autoby default;use_responses_lite— on by default; setfalseto send tools in the classic Responses shape instead of the lite envelope;codex_session_id,codex_thread_id,codex_window_id,turn_state— to pin a continuation explicitly instead of letting the adapter recover it from message metadata.
make verifyThat resolves the module's and the harness's public Wippy dependencies to their
current releases (wippy update in both; locks are generated, never
committed), installs the UI from package-lock.json, validates identity
consistency, documentation links, dependency ranges, generated files, frontend
registry metadata, and secret hygiene, runs Wippy lint, runs strict
Vue/TypeScript checking and the production web-component build, then boots the
standalone harness and runs every suite.
The module holds no database, so there is no migration and no PostgreSQL matrix. Adding persistence means restoring both.
Install the current Wippy CLI from Wippy releases and use Node.js 22 or newer. Confirm both before starting:
wippy version
node --versionPublishing requires a Wippy account with access to the butschster
organization:
wippy auth login
wippy auth status
make release-check
make publishmake publish creates a private plugin by default and embeds the built UI. To
publish publicly:
make publish VIS=publicThe source manifest does not pin a release version. The publisher selects the
next valid version; published releases remain immutable. Runtime dependencies
use compatibility constraints, while wippy.lock files carry exact resolved
artifacts for reproducible execution.
Bootstrap Kickside in a separate directory (first boot runs clean, without the overlay, so the resolved graph and admin account exist):
mkdir ../kickside-host
cd ../kickside-host
wippy run kickside/kickside -c \
--profile bootstrap_admin --profile local --profile sqlite \
--set vars.local_port=8090 \
--set vars.local_public_api_url=http://localhost:8090 \
--set vars.bootstrap_admin_email=admin@example.com \
--set vars.bootstrap_admin_password=change-meStop it once it settles, then create the untracked
../kickside-host/.wippy.workspace.yaml:
version: "1.0"
workspace:
replacements:
butschster/llm-codex: ../llm-codex
override:
"app.env:defaults:values.GOV_MANAGED_NAMESPACES": "butschster.llm_codex"
"butschster.llm_codex.security:user_security_scope:default": app.security:user
"butschster.llm_codex:codex_home:data.directory": /home/butschster/.codexThe user_security_scope line binds the module's security requirement to the
application's authenticated-user group; without it every module endpoint
returns 403. The codex_home line is the development stand-in for the
codex_home_path requirement an installed module is asked for. Restart the
host from its directory with the overlay (bare wippy run reads the locked
graph; keep the same profiles and vars):
wippy run --config .wippy.workspace.yaml -c \
--profile bootstrap_admin --profile local --profile sqlite \
--set vars.local_port=8090 \
--set vars.local_public_api_url=http://localhost:8090 \
--set vars.bootstrap_admin_email=admin@example.com \
--set vars.bootstrap_admin_password=change-meThe full loop, including Keeper-driven reactive development, is documented in The Dev Loop.
The host stays source-free: its lock and vendor packs belong to the deployment;
this checkout is the only local source. Never add local replacements to
wippy.lock and never point Keeper's application filesystem sync at a
conventional module src/ tree.
butschster.llm_codex:definitionis the authoritative rootns.definition.butschster.llm_codex:codex_homeis the filesystem resource the credential cache is read through;codex_home_pathsupplies its absolute path.butschster.llm_codex.auth:credentialsreads and rotates the ChatGPT credential cache. It is the only place token material exists.butschster.llm_codex.client:transportis the Responses transport; it reuses Wippy's canonical SSE parser rather than forking the event grammar.butschster.llm_codex.client:mapperlayers Codex continuation metadata onto the native OpenAI Responses mapper.butschster.llm_codex:driverbindswippy.llm:generatorandwippy.llm:providertobinding:generate_funcandbinding:status_func.butschster.llm_codex:provideris thellm.providermodel cards point at.butschster.llm_codex.catalog:catalogtranscribes the Codex model cache intollm.modelentries;service:catalog_syncapplies it on boot and every five minutes.butschster.llm_codex.modelsis the namespace those mirrored cards are addressed in. The module ships no declarations there: every entry is created and retired at runtime from the cache.butschster.llm_codex.api:get_status.endpointandpost_sync.endpointare the authenticated HTTP surface.butschster.llm_codex:llm_codex_viewpublishes an announced, auto-registered Wippy web component served by the module's own embedded filesystem.test/supplies an isolated host, a Codex home fixture, and the wiring and cache suites.
Package identity (organization/module), registry namespace
(namespace:name), and component instance IDs are different identities. Do not
derive one from another. The root ns.definition declares the namespace.
This module owns the Codex transport and nothing else. It does not reimplement what the platform already owns:
- LLM contracts, the universal prompt and response shapes, streaming output,
and the Responses SSE grammar come from
wippy/llm. - The OpenAI Responses client and mapper are imported from
wippy.llm.openai, not copied. - Model classes, DB provider profiles, and model resolution stay with
kickside/models; the mirror only publishes registry-levelllm.modelcards.
Contract definitions, bindings, and ports are documented in Contracts And Ports; model discovery and classes in Agents, Skills, And Models.
AGENTS.md development instructions
.kickside-module.json module identity
scripts/ deterministic validation
wippy.yaml publish manifest; no fixed release version
src/ registry declarations and Lua implementation
ui/ source for the Wippy web component
static/ generated, committed publish artifact
test/ standalone Wippy harness and Codex home fixture
docs/kickside-development/ Kickside developer Wiki snapshot
.github/workflows/verify.yml registry, suites, and frontend CI
Start with AGENTS.md, even when you are not using an agent. The handbook begins at Developer Handbook; frontend work begins at Frontend Handbook.
- Never commit credentials, an
auth.json,.env,.wippy/, a rootwippy.lock, module packs,node_modules, or source maps. - Never widen the credential surface: token material stays inside
src/auth/credentials.lua, and an identity-provider response body never reaches a caller-visible error or a log line. - Never put an exact resolved version in an
ns.dependency; exact versions belong in lock files. - Never infer a registry namespace from a package name. Read
ns.definition. - Never add compatibility fallbacks or duplicate ownership to hide a broken contract.
- Never manufacture actor scope. Execution inherits the calling actor.
- Build UI from
ui/src; do not hand-editstatic/. - Use Wippy theme tokens and host APIs; no hardcoded deployment paths, raw
proxy wires, fake
--p-*colors, or unowned host styling.
The bundled handbook is a public, offline-readable snapshot of the published Kickside Wiki. When the snapshot and the Wiki disagree, the Wiki wins.