Skip to content

Repository files navigation

butschster/llm-codex

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.provider registry entry and the contract.binding that implements wippy.llm:generator and wippy.llm:provider;
  • a Responses transport for https://chatgpt.com/backend-api/codex with 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.model registry entries and retires the ones the service stops listing;
  • an authenticated status page showing the sign-in state, cache freshness, and mirrored models.

How authentication works

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_path requirement, because no default this module could invent would be true.

Install into a Kickside host

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.

Use the provider

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, auto by default;
  • use_responses_lite — on by default; set false to 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.

Verification

make verify

That 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 --version

Publish to the Wippy Hub

Publishing requires a Wippy account with access to the butschster organization:

wippy auth login
wippy auth status
make release-check
make publish

make publish creates a private plugin by default and embeds the built UI. To publish publicly:

make publish VIS=public

The 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.

Develop against a Kickside host

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-me

Stop 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/.codex

The 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-me

The 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.

What is in the module

  • butschster.llm_codex:definition is the authoritative root ns.definition.
  • butschster.llm_codex:codex_home is the filesystem resource the credential cache is read through; codex_home_path supplies its absolute path.
  • butschster.llm_codex.auth:credentials reads and rotates the ChatGPT credential cache. It is the only place token material exists.
  • butschster.llm_codex.client:transport is the Responses transport; it reuses Wippy's canonical SSE parser rather than forking the event grammar.
  • butschster.llm_codex.client:mapper layers Codex continuation metadata onto the native OpenAI Responses mapper.
  • butschster.llm_codex:driver binds wippy.llm:generator and wippy.llm:provider to binding:generate_func and binding:status_func.
  • butschster.llm_codex:provider is the llm.provider model cards point at.
  • butschster.llm_codex.catalog:catalog transcribes the Codex model cache into llm.model entries; service:catalog_sync applies it on boot and every five minutes.
  • butschster.llm_codex.models is 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.endpoint and post_sync.endpoint are the authenticated HTTP surface.
  • butschster.llm_codex:llm_codex_view publishes 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.

Reuse boundaries

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-level llm.model cards.

Contract definitions, bindings, and ports are documented in Contracts And Ports; model discovery and classes in Agents, Skills, And Models.

Repository map

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.

Rules

  • Never commit credentials, an auth.json, .env, .wippy/, a root wippy.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-edit static/.
  • Use Wippy theme tokens and host APIs; no hardcoded deployment paths, raw proxy wires, fake --p-* colors, or unowned host styling.

Documentation provenance

The bundled handbook is a public, offline-readable snapshot of the published Kickside Wiki. When the snapshot and the Wiki disagree, the Wiki wins.

About

Native Wippy LLM provider backed by the official Codex CLI ChatGPT sign-in

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages