For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /reference/docs/decisions/0021-themelio-cli.md.

ADR-0021: Themelio Command-Line Interface

Status: Draft

Date: 2026-07-06

Deciders: TBD

Relates to: ADR-0011 (Human Web UI and Agent Client Access) — the CLI is a new programmatic client alongside the web UI; ADR-0012 (Job and Deployment Management System) and ADR-0013 (actions.yaml Automation System) — the CLI drives and observes the jobs/deployments and actions those ADRs introduced.

Context

Themelio today is reachable only through its Next.js web UI. Machine-learning practitioners, CI pipelines, and agent clients increasingly expect a terminal-driven, scriptable client — the way Hugging Face users reach the Hub through the hf CLI. We want a first-class themelio command-line tool that covers the resource lifecycle the platform already exposes (auth, repositories for models/datasets/spaces, running actions, and observing the resulting jobs and deployments) without inventing new backend architecture.

Current State

  • The web service (services/web) exposes an HTTP API under /api/** for models, datasets, spaces, repos, jobs, deployments, and settings. It backs onto GitLab (GraphQL/REST) and proxies jobs/deployments to k8s-root-actor.
  • Authentication is a single PRIVATE-TOKEN header. In the browser flow, NextAuth (OIDC/Keycloak) mints a GitLab impersonation token stored in Valkey keyed by an opaque, httpOnly session cookie; middleware injects that token as PRIVATE-TOKEN. Users can also mint a personal access token in Settings → Access Tokens via POST /api/settings/tokens, fulfilled by gitlab-root-actor against GitLab's admin endpoint.
  • Content (repository files, including large files) lives in GitLab and is transferred over git-HTTPS + LFS. The API's POST …/commit file-write endpoints are stubs (they return fabricated commits and persist nothing).
  • Compute is never created directly: running an actions.yaml action (ADR-0013) yields a job (slurm-job) or a deployment (k8s-deployment / k8s-inferenceservice), persisted per ADR-0012. The server validates action parameter values at execution time but performs no structural validation of actions.yaml, and resolving a ref: requires the shipped references that live in k8s-root-actor.

Current Problems

  1. No terminal/scriptable client. Every operation requires the browser UI; there is no way to script repository, action, or job/deployment workflows, or to drive Themelio from CI or an agent.
  2. No documented headless authentication path. The browser cookie flow is unusable outside a browser, and no client-facing token model is written down for programmatic use.
  3. No content-transfer story for a client. With commit endpoints stubbed, a client has no API to push or pull repository bytes.
  4. No way to validate actions.yaml before running it. Authors only discover structural mistakes when an action fails at execution time.

Requirements

  1. Authenticate non-interactively (CI) and interactively, using a credential that works for both the web API and git content transfer.
  2. Cover the existing resource lifecycle: create/fork/list/inspect repositories; run actions; observe and destroy jobs and deployments.
  3. Be scriptable: machine-readable output and stable exit codes.
  4. Add the minimum viable backend surface; reuse existing services and semantics.

Decision Drivers

  • Familiarity — behave like hf where it fits, so ML users are immediately productive.
  • Thin client — keep logic and validation on the server; the CLI orchestrates and formats.
  • Reuse over rebuild — lean on the existing web API, actions.yaml, and jobs/deployments rather than new architecture.
  • One credential — a single token for both API calls and git.
  • Scriptability — first-class --json and predictable exit codes.

Considered Options

Option A: A Python themelio CLI over the web API, with git hand-off for content

A standalone Python 3.11+ CLI (themelio, distributed via pipx/uv, built on Typer + httpx) that is a thin client of the web API. Its design rests on five sub-decisions.

  1. Authentication — paste an api-scoped GitLab PAT. The user creates a personal access token in Settings → Access Tokens with the "Full API Access" (api) scope and pastes it into themelio auth login (or supplies THEMELIO_TOKEN/--token for CI). One api-scoped token covers everything, because GitLab's api scope also authorizes git push/pull over HTTPS. To enable this, the Settings token UI was extended to offer the api scope (previously git read/write only). The CLI stores the token in the OS keyring by default (falling back to ~/.themelio/config, mode 600) and verifies it via GET /api/whoami-v2. The host defaults to https://dev.themelio.pharos.grnet.gr and is confirmed at login.

    Two alternatives were weighed and rejected: reusing the browser session cookie (it is opaque, httpOnly, browser-tethered, revoked on browser sign-out, and meaningless to GitLab, so it cannot authenticate git); and a browser OIDC device-login flow (nicer UX but requires new backend endpoints — deferred as a future enhancement, not v1).

  2. Content transfer — hand off to the user's git. Because the commit endpoints are stubs and content lives in GitLab, the CLI moves no bytes itself. themelio git-setup prints (or, with --apply, writes) a git credential-helper configuration so git clone/git push over HTTPS transparently use the stored token via a hidden themelio git-credential subcommand. <type> <ns/repo> download and upload print the exact git/git-lfs commands for a repo rather than executing them, keeping the CLI thin and free of a git runtime dependency.

  3. Uniform command grammar. Repo-scoped commands read themelio <type> <ns/repo> <verb> (the repo reference immediately after the type). Every resource type (models, datasets, spaces, repos) exposes the same core verbs — create, fork, ls, info, card, settings, ls-files, download, upload — with no type-specific verbs.

  4. All compute flows through actions. models, spaces, and repos carry an actions subgroup; <type> <ns/repo> actions run is the create-verb that yields a job or a deployment. jobs and deployments are observe-only (ls, get, logs, wait, destroy). This mirrors ADR-0012/0013 exactly: there are no ad-hoc "run a container" or per-type "deploy" verbs.

  5. A new server-side validation endpoint. <type> actions validate posts a local actions.yaml to a new POST /api/actions/validate endpoint. It reuses the web executor's existing validation logic (action-loader.ts + action-parameters.ts) and delegates ref: resolution to k8s-root-actor (populateRefs), so the CLI and the web authoring UI share one source of truth and the CLI never reimplements rules it cannot fully evaluate locally.

Pros:

  • Familiar, hf-like ergonomics with a uniform, predictable grammar.
  • A single api-scoped token drives both the API and git; no bespoke credential store or new auth backend.
  • Thin client: validation and orchestration stay on the server; the CLI formats and hands off.
  • Reuses actions.yaml, jobs, and deployments unchanged; only one small additive endpoint.
  • Python + pipx/uv matches the ML audience's tooling.

Cons:

  • Content transfer depends on the user having git/git-lfs installed and configured (mitigated by git-setup).
  • Pasting a PAT is less slick than a browser device-login (deferred).
  • Some hf-familiar conveniences are intentionally absent (no local content cache; no move/branch/tag/delete-files) because the backend does not support them yet.
  • One new endpoint (POST /api/actions/validate) must be built before actions validate works.

Decision

We will build Option A: a Python themelio CLI that is a thin client of the web API, authenticated by a pasted api-scoped GitLab personal access token, using a uniform themelio <type> <ns/repo> <verb> grammar, driving compute exclusively through actions.yaml (yielding observe-only jobs and deployments), and handing repository content transfer off to the user's own git/git-lfs (configured via themelio git-setup). We will add one backend endpoint, POST /api/actions/validate, to back actions validate. The Settings token UI has already been extended to offer the api scope required by the CLI.

See also: DD-0021: Themelio CLI — Implementation

Consequences

Positive:

  • Themelio gains a scriptable, CI- and agent-friendly client without new architecture.
  • One credential (api PAT) works across the web API and git; keyring storage by default.
  • The action-driven command model keeps the CLI and platform semantics aligned (ADR-0012/0013), avoiding a divergent "run" surface.
  • Server-side actions.yaml validation benefits the web authoring UI too.

Negative:

  • Users must have git/git-lfs available for content transfer.
  • Interactive login is a manual copy-paste until (and unless) a device-login flow is built.
  • The CLI cannot offer repository delete, file-level commits, branches, or tags until the corresponding endpoints exist.

Neutral:

  • The CLI is a separate deliverable (its own Python package), not part of the pnpm monorepo build.
  • Jobs/deployments are addressed by id within a repo scope, so those commands take --repo.

Migration

No migration of existing infrastructure is required. The work is additive: a new client package, one new additive endpoint (POST /api/actions/validate), and an already-shipped, backward-compatible addition of the api scope option to the Settings token UI. No data stores, schemas, or existing endpoints change.

Open Questions

  1. A browser device-login flow (reusing OIDC + impersonation-token machinery to remove the copy-paste step) is a candidate future enhancement, out of scope for v1.

References

Page source

Source snapshot · Revision f928834c8.