Start here
Local CLI and operator UI
Install and use the offline-first Chalupa CLI without bypassing its Taskfile, confirmation, or secret boundaries.
Reviewed 2026-09-06
The Chalupa CLI is the operator entrypoint for the repository's existing
workflow. It can inspect a configuration, read bounded local state, and
delegate approved actions to public Taskfile commands. It does not implement a
second Pulumi lifecycle. With provider: chalupa, GPU commands instead call
the console queue using the local account session; no provider token is needed.
Everything an operator does to a specific environment is a chalupa command:
chalupa up, chalupa tunnel, chalupa seed, chalupa down, and the rest of
the allowlist. Repository work stays with task inside the checkout:
task setup, task typecheck, task test, task validate, task preview,
task ci-preview, task report, task cloud:*, the teardown runner and
schedule tasks, and task data-nuke. The CLI never replaces the Taskfile; it
selects one of its public actions and passes a canonical config path for BYOC.
Managed GPU uses the console API and a pinned local SSH record.
Opening the interface does not contact DigitalOcean, read tvault, select a Pulumi stack, start Docker, or open SSH. An external process runs only after you explicitly request an action or refresh local status.
Run from a checkout
Install the locked dependencies, then invoke the CLI through Bun:
cd "$HOME/projects/chalupa"
bun install --frozen-lockfile
bun run chalupa -- doctor
bun run chalupa -- inspect
The extra -- separates Bun's arguments from Chalupa's arguments. This is the
supported no-link workflow and always runs the source in the current checkout.
For a user-local chalupa command linked to the same checkout:
cd "$HOME/projects/chalupa"
bun link
chalupa --version
bun link registers this checkout's declared binary, which is what puts
chalupa on your PATH. After that, operator commands work from your own
project directory: you no longer need to cd into the Chalupa checkout, and
the machine running them does not need Task installed for the CLI itself to
start. Task is still required for any action that reaches the cloud, because
the CLI delegates to it; chalupa doctor reports that separately.
The link points at this checkout's declared binary. To update it, update the checkout and reinstall its locked dependencies:
cd "$HOME/projects/chalupa"
git pull --ff-only
bun install --frozen-lockfile
chalupa --version
Review local changes before pulling. bun link is optional; every example
below also works as bun run chalupa -- <arguments>.
Use the project in your current directory
Interactive commands find chalupa.yml in the current directory or an ancestor,
stopping at the Git root. Explicit --config and the existing configuration
environment variables take precedence. No billable operation falls back to the
bundled demo. Scripts using --confirm still need --config on the same command
line so the target and authorization remain explicit.
chalupa up
chalupa exec -- bun run dev
chalupa down
up checks local port availability before provisioning, then opens temporary
tunnels to check the configured health endpoint. Without compute.health,
it reports tunnel availability and leaves application readiness unverified.
GPU inference also checks installed model metadata; a real turn is needed to
observe loaded GPU placement. Readiness failures can leave compute running.
exec runs the supplied command locally, in your working directory, using
the existing environment. It opens the Taskfile-owned tunnels and closes them
on exit. It never creates or destroys compute, never rewrites .env, and never
infers database passwords or URLs. Existing app connection settings should use
the local ports shown in the connection output. The child also receives
CHALUPA_ENVIRONMENT and CHALUPA_PORT_<remote-port> values.
For inference, chalupa launch (using inference.agents.default) configures the endpoint for your
agent's process. See Agent harnesses for supported tools
and chalupa exec -- my-agent for other commands. The command's stdout stays available for output
or JSON receipts; Chalupa's connection messages go to stderr. Ctrl-C cancels
the session, and a lost tunnel stops the local process. Compute remains running.
For a remote shell, use chalupa ssh. Image builds and live file synchronization
are not provided by exec; selected Compose services require pre-built images.
Check the workstation
Run the executable check before operating an environment:
chalupa doctor
chalupa doctor --json
doctor reports offlineReady separately from operatorReady. The offline
inspection path is ready when the CLI itself is running. Full operation also
requires Task, Pulumi, tvault, SSH, and nc to be discoverable on PATH.
doctor does not execute those tools or query credentials, Docker, the
network, local stacks, or a cloud provider. A missing operator requirement
produces a non-zero exit while the report still identifies offline inspection
as available.
Inspect safely first
With no config, inspect uses the repository's examples/demo-harbor
fixture:
chalupa inspect
chalupa inspect --json
This is synthetic offline mode. It runs the same Compose inference as the Pulumi program and reports services, inferred roles, tunnel ports, named volumes, persistence, diagnostics, and the complete redacted remote Compose document. It does not run Task or any external process.
Redacted output is still operator information. Absolute paths, service names, topology, port numbers, images, and non-secret metadata can remain visible. Do not publish the output or treat it as a general data-loss-prevention scan.
Select a real configuration explicitly:
export DEMO_CONFIG="/absolute/path/to/project/chalupa.yml"
chalupa inspect --config "$DEMO_CONFIG"
chalupa tui --config "$DEMO_CONFIG"
Prefer --config in scripts and operator notes so the selected environment is
visible at the call site.
Commands find ./chalupa.yml, then CHALUPA_CONFIG, then parent configs.
--config PATH is an optional override. Legacy Taskfile config variables retain
precedence. Managed up and down ask yes/no; use --yes for scripts.
Command reference
| Command | Behavior |
|---|---|
chalupa |
Opens the operator UI when both input and output are interactive terminals. Prints help when invoked with no arguments outside a TTY. |
chalupa doctor [--json] |
Reports separate offline and full-operator readiness without running external executables. |
chalupa inspect [--config PATH] [--json] |
Builds a local inspection. Without a config, it uses the synthetic fixture. |
chalupa status [--config PATH] [--json] |
Reads local Pulumi status for BYOC; managed configs query the console and print the host and expiry. |
chalupa tui [--config PATH] |
Opens the interactive operator UI. An explicit invocation fails outside a TTY. |
chalupa setup [--inference] [--no-console] [--no-reaper-token] [--provider chalupa|digitalocean|hetzner] [--force] [--json] |
Managed GPU is the first/default choice: local key, account login, starter, balance and next command. --provider chalupa skips selection and implies inference. The current operator device flow cannot issue a managed account session; see Quickstart. BYOC adds provider credentials and an SSH allowlist. Inference offers to arm the console reaper using a separate token with only droplet:read and droplet:delete. --no-reaper-token skips arming; --no-console skips linking too. JSON mode submits only when CHALUPA_SETUP_REAPER_TOKEN is set and an account is linked. |
chalupa exec [--config PATH] -- COMMAND [ARGS...] |
Runs a local command through managed tunnels; closes tunnels on exit and retains compute. |
chalupa up [--yes] [--config PATH] |
Managed: shows price, estimate and balance, rejects insufficient credit before queueing, and asks yes/no on a TTY. BYOC delegates to Task. |
chalupa down [--yes] [--config PATH] |
Managed: asks yes/no, stops compute and retains the cache. BYOC scripts use an exact --confirm phrase and explicit config. |
chalupa launch [opencode|omp|local-agent] [--config PATH] [--context auto|native|TOKENS] |
Uses inference.agents.default; context defaults to auto. Opens and closes its own tunnel. |
chalupa tunnel [--config PATH] |
Holds a standalone SSH tunnel until interrupted. |
chalupa preview [--config PATH] |
Shows the project configuration and estimate offline. |
chalupa ACTION [--config PATH] |
Delegates one allowlisted public action to Task from the Chalupa repository root. |
chalupa ci arm|follow|cancel|report|artifacts [--config PATH] |
Delegates a namespaced ci: action. |
chalupa ci status [--config PATH] [--json] |
Reads bounded CI run state from the environment, the ci: sibling of chalupa status. |
chalupa teardown register [--config PATH] |
Delegates the namespaced teardown:register action. |
chalupa account [--json] |
Reads a redacted hosted-account snapshot from local files only. |
chalupa up [--model TAG] [--tier gpu-small|gpu-large|gpu-max] [--region tor1|nyc2|ams3|atl1] [--yes] [--json] [--config PATH] |
Managed launch with optional model/tier/region overrides for this run; validates VRAM fit and saves choices in the managed record without editing YAML. Checks capacity before queueing; offers alternatives or a 60-second poll for up to 20 minutes. Non-TTY/JSON needs --yes or CHALUPA_YES=1; no capacity prints alternatives and exits 1. |
chalupa tunnel [--config PATH] |
Managed configs use the pinned host key in the local record and forward Ollama to port 11435 by default. Ctrl-C closes the tunnel. |
chalupa down --confirm "down <slug>" [--config PATH] |
Managed configs queue teardown and remove the local record after success. BYOC retains its existing sink confirmation. |
chalupa session allow [--config PATH] |
Managed only: detect the current IPv4 /32 and update SSH access. Managed session extension is not available yet. |
chalupa balance [--top-up 10|25|50] [--json] |
Shows the Managed GPU prepaid balance and rates, or opens Stripe Checkout for a top-up; needs a linked account. |
chalupa audit [--json] |
Read-only DigitalOcean inventory: Chalupa droplets, volumes, snapshots, firewalls and keys, their estimated cost, project membership and leftovers. Never changes anything. |
chalupa gc [--json] [--delete NAME --confirm "reclaim NAME"] |
Dry run of compute no local stack tracks (with $/h); --delete reclaims one leftover after the literal confirmation and verifies the provider no longer lists it. Storage stays. |
chalupa login [--url ORIGIN] [--config PATH] |
Legacy operator-only device authorization; an administrator must approve the terminal. |
chalupa open [console|billing] [--url ORIGIN] |
Opens a hosted view through a short-lived, one-use browser grant. |
chalupa logout |
Revokes hosted sessions and removes both local credential files. |
chalupa run ACTION [--config PATH] |
Compatibility form of the same actions, spelled with the exact Taskfile name. |
An action whose Taskfile name contains a colon is typed as two words, so
chalupa ci arm delegates to task ci:arm and chalupa teardown register
delegates to task teardown:register. Both spellings resolve to the same
allowlist entry, risk classification, confirmation phrase, and TTY rule;
chalupa run ci:arm remains accepted for scripts written against the earlier
surface.
The CLI passes only the canonical config path to Task. Other Taskfile inputs
are read from the environment, so a dedicated key is selected with
export SSH_IDENTITY=/absolute/path/to/private-key before the command.
Verbosity
Every action honours one verbosity control, spelled the same way on the CLI and on the Taskfile:
| Spelling | Where |
|---|---|
-q, --quiet |
chalupa ACTION -q / chalupa run ACTION -q |
-v, --verbose |
chalupa ACTION -v / chalupa run ACTION -v |
CHALUPA_VERBOSITY=quiet|verbose |
environment, read when no flag is passed |
VERBOSITY=quiet|verbose |
task ACTION VERBOSITY=quiet directly |
A flag always wins over the environment; omitting both leaves the readable
default. Verbose is exactly the historical firehose: Pulumi's own preview
table and per-resource progress on data-up/up/down, and ci follow's
raw chalupa-ci {...} NDJSON line per engine event. The default level
captures Pulumi's transcript and prints resource counts, duration, warnings,
and (for up/data-up) the ip/priceHourly/providerId or
volumeName/volumeId outputs, instead of the whole thing; it renders ci follow's journal as one line per step — verdict, duration, and a running
tally — instead of raw JSON. Quiet shortens a clean success further (a
single line for Pulumi; passing steps go unprinted on ci follow, though
the tally still counts them).
None of this changes what a failure shows: at every level, a failed
pulumi up/down prints the complete transcript it always printed, in
full, before the task exits non-zero. A diagnostic is never harder to reach
than it was before this control existed.
status reports what the local Pulumi backend knows about two exact stack
names. Compute is absent, empty, or present. Protected data is
not-configured, absent, present, or unknown; configured states include
the expected size from chalupa.yml, while a present state also includes the
bounded local volume name and ID. The expected size is not a live provider
capacity reading.
The read path does not select a stack, load a provider credential, or query
DigitalOcean. An unexpected data stack, invalid outputs, inconsistent config,
or failed read becomes unknown instead of being treated as absent. Local
state can still be stale; the provider console and invoice remain
authoritative.
Actions and confirmations
Only these public actions are accepted, whether they are typed directly or
through chalupa run. The Actions screen shows the same set plus the bounded
local-status and CI-status refreshes:
| Command | Taskfile action | Risk | Exact phrase |
|---|---|---|---|
chalupa preview |
preview |
offline | none |
chalupa validate |
validate |
offline | none |
chalupa data-up |
data-up |
billable persistent data | persist <environment> |
chalupa up |
up |
billable compute | launch <environment> |
chalupa agent-bootstrap |
agent-bootstrap |
changes the provisioned host | bootstrap <environment> |
chalupa tunnel |
tunnel |
read-only connection | none |
chalupa ssh |
ssh |
read-only interactive session | none |
chalupa seed |
seed |
changes application data | seed <environment> |
chalupa teardown register |
teardown:register |
changes the local expiry registry | register <environment> |
chalupa ci arm |
ci:arm |
billable suite time | test <environment> |
chalupa ci follow |
ci:follow |
read-only log stream | none |
chalupa ci cancel |
ci:cancel |
stops the active run | cancel <environment> |
chalupa ci report |
ci:report |
publishes the settled run; idempotent | none |
chalupa ci artifacts |
ci:artifacts |
read-only artifact index | none |
chalupa down |
down |
destroys disposable compute | sink <environment> |
<environment> is the exact resolved environment name. Interactive up
and down use a yes/no prompt, defaulting to No; select Yes or press y to
continue. Escape, Ctrl-C, a closed terminal or No cancels. Other changing
actions retain their exact typed phrase. For non-interactive use, the table
lists the case-sensitive --confirm phrase; a mismatch refuses before Task.
Before each confirmation, Chalupa prints a target summary with the
redacted canonical config path, exact compute stack, region, and size.
data-up also shows the exact <environment>-data stack and configured
expected GiB. down shows the protected-data stack and states that it
survives.
For example:
chalupa up --config "$DEMO_CONFIG"
Every changing or long-running action requires an interactive terminal.
preview and validate are the only actions available in synthetic mode and
the only actions accepted outside a TTY. status --json remains available to
non-interactive consumers when they supply an explicit config.
What --confirm actually is
chalupa ACTION --config PATH --confirm "<phrase>" supplies exact
environment-bound authorization as a literal command-line argument — so a script, cron job, or another
tool can run a billable or destructive action without a terminal attached.
Be honest about what this buys you: it is friction against a human running
the wrong command against the wrong environment, not authentication. The
phrase is not a secret — it is deterministically derived from the resolved
config's own name field ("launch " + config.name, "sink " + config.name,
and so on), so anyone who can read chalupa.yml can compute it, and
src/cli/chalupa.ts prints the exact expected phrase back on a mismatch
(The supplied --confirm phrase does not match; expected exactly "...") —
deliberately, because hiding it would only turn a typo into a support
question instead of a stopped command. What --confirm actually enforces is
that the target and the authorization are stated together, explicitly, in
the one invocation that uses them (--confirm requires --config on the
same command line; an inherited CONFIG does not count), so an automation's
own config carries a phrase written once on purpose rather than a credential
that could leak or be replayed against a different environment. See chalupa docs non-interactive for the complete rule set.
Sign in to the console
chalupa setup, chalupa login, chalupa open, and chalupa logout are the
commands in this flow that reach the network; every other command stays
offline until it delegates to Task. They link this workstation to the hosted
control plane rather than to any specific environment.
For an ordinary customer account, use setup. It signs in or creates the tenant account used by billing and organization views:
chalupa setup
chalupa account --json # offline, with session tokens redacted
chalupa open
chalupa open billing
chalupa logout
chalupa login is not the customer sign-in command. It is the separate,
legacy operator-only device flow and requires an administrator to approve the
terminal:
chalupa login --config ./chalupa.yml # or --url https://chalupa.example
setup writes the verified tenant-account session to
~/.config/chalupa/account.json; this is the account used by chalupa account
and chalupa open. login is a separate, legacy operator-only device flow.
It writes a revocable terminal credential to session.json only after an
administrator approves the displayed code. A tenant session in account.json
never auto-approves that device flow or grants operator access. Both files have
owner-only (0600) permissions. When the tenant account session is still
valid, open exchanges it directly for a short-lived, one-use browser grant.
open billing requests the billing destination from the same allowlisted
endpoint. A stored credential is never
sent to an origin different from the one that issued it.
account and the TUI's Hosted screen merge those files into a local,
redacted status view without contacting Chalupa. The view can include
cached plan and managed-hour fields, always marked fresh or stale; the hosted
billing view is authoritative. Session tokens are deliberately absent from
both text and JSON output. logout attempts to revoke both sessions and
removes the local copies even when the control plane cannot be reached.
The stored credentials are scoped to hosted access, not to Chalupa's provider
access: neither is the DigitalOcean token, neither is printed, both expire,
and the terminal credential can be revoked early from Terminals in the
console. --url
selects the control-plane origin directly; without it, Chalupa falls back in
order to CHALUPA_CLOUD_URL, then the selected config's cloud.url, then the
origin from a valid setup account or a previous chalupa login on this
machine (even if that terminal credential has expired — the origin it named is
still meaningful), and finally to https://chalupa.run, the one production
control plane, so a bare chalupa login on a fresh machine has somewhere to
go. chalupa login prints
which of the last two it used, since both are a guess rather than something
you told it. Nothing about login changes what the Taskfile allowlist can
do — it only lets open skip a manual sign-in.
Provider-bound sinking
Both surfaces bind down to the provider ID of the compute they most
recently observed, and pass it to the Taskfile as a typed
EXPECTED_PROVIDER_ID argument. The provider-scoped destroy task compares
the current stack output with that value immediately before destruction and
refuses a mismatch. Where they differ is what happens without a fresh
binding.
The operator UI requires the operator to refresh status first: press r
immediately before selecting Sink compute. The UI accepts only a
present compute status observed within the last two minutes with a valid provider ID,
and shows that ID in the target summary. Without that fresh observation,
sinking stays disabled — the UI refuses rather than proceeding unbound.
Chalupa also revalidates the same two-minute observation immediately after
the confirmation; a prompt left open past the deadline is cancelled before
Task starts and requires another status review.
Direct chalupa down captures its own fresh status immediately before
running, with no separate refresh step required, and binds to the reported
provider ID exactly as the UI does. When that capture fails, or the reported
compute carries no provider ID, chalupa down proceeds anyway, bound only by
the configured local stack identity, and prints a warning
(Provider binding unavailable; down stays bound to the stack name only.)
before it runs. It still requires interactive yes/no confirmation or the exact
--confirm "sink <environment>" phrase and
uses the normal compute-only Taskfile path. Prefer the UI when you want a
missing binding to block the destroy instead of just being warned about it.
data-nuke is intentionally unavailable in the CLI and operator UI. Permanent
data deletion remains an independent, explicitly confirmed Taskfile operation
documented in Lifecycle. Never reproduce it
with direct Pulumi commands.
Navigate the operator UI
The interface follows the same deep-green and teal visual language as
chalupa.run. It adapts to light and dark terminals, uses a bounded 256-color
fallback, and returns to terminal-default styling when theme or color support
cannot be established safely. Set NO_COLOR=1 to disable styling. Status,
risk, selection, and availability always retain text markers, so color is
never the only signal.
The full-screen interface requires a capable interactive terminal. When
TERM=dumb, use the linear chalupa inspect and chalupa status commands and
the direct actions instead; Chalupa will not force an alternate-screen UI onto
that terminal.
The landing screen is Home, and it shows a session rather than a parsed configuration. A session is four separate observations, in order:
| State | What proves it |
|---|---|
| Configured | A chalupa.yml was read offline. |
| Provisioned | The local Pulumi backend reports live compute. |
| Reachable | The forwarded Ollama answered on 127.0.0.1. |
| Model ready | The declared model is installed or loaded, with its GPU placement. |
Each state carries its own observation and its own age. A state this laptop has
not observed reads UNKNOWN; it is never inferred from a neighbouring one, and
an observation older than two minutes reads STALE rather than as fact. Home
also names exactly one next action — "Launch GPU to start a session", "Open
agent", "Launch an agent", "Sink to stop billing" — so the interface has an
answer to "what do I press now".
Which screens exist depends on what the configuration declares. An inference environment shows Home, Model, Actions, and Help. A Compose or CI environment shows Home, Preview, Status, CI, Actions, Hosted, and Help.
- Home — session state, cost, expiry, the endpoint an agent uses, and the primary actions.
- Model — the tunnelled endpoint: daemon version against the pin, installed or loaded state, digest, quantization, declared and loaded context, model ceiling, GPU placement, weight size, and the endpoint URLs an agent needs.
- Preview — the complete redacted remote Compose document.
- Status — independent compute and protected-data state from the local Pulumi backend.
- CI — the declared suite, bounded run state, connection state, and budget.
- Actions — the Taskfile allowlist, narrowed to the blocks this configuration declares, with risk classification.
- Hosted — the redacted local account/session cache and usage freshness; opening it never makes a network request.
- Help — safety boundaries, what each session state means, and the keyboard reference.
Home's primary actions are Launch GPU (up), Open agent, and Sink
(down), plus Install expiry and Register expiry when the config
declares a session: block. Open agent asks which agent to run and then runs exactly what chalupa launch runs: the
UI suspends, the managed tunnel opens, the agent takes the terminal, and the
agent's own settings are never modified. When the agent exits, the UI says what
is true: the GPU is still running and still billed.
Every screen of an inference session carries the same chrome line: elapsed
time, the hourly rate, the estimated cost so far, and how much of the declared
session budget is left. Elapsed time is measured from the first moment this
window observed the box — status:json carries no creation time — and it is
labelled as an observation, not as uptime. Whether the local teardown scheduler
actually holds this deployment is not something the UI reads, so it says
armed state unknown and keeps a banner up: "Expiry is not armed. This GPU
runs until you sink it."
Use these keys:
| Key | Effect |
|---|---|
1–8 or o, m, p, s, c, a, u, ? |
Open a visible screen directly. |
Left, Right, h, l, or Tab |
Move between screens; Shift-Tab moves backward. |
Up, Down, j, or k |
Move through actions or scroll the current screen. |
| Page Up or Page Down | Scroll by one visible page. |
| Home or End | Jump to the beginning or end of the current screen. |
| Enter | Run the selected enabled action. |
r |
Refresh what the current screen observes. |
x or Backspace |
Dismiss the current notice, or close the agent chooser. |
| Escape | Return to Home. |
q |
Exit while the operator UI owns the terminal. |
| Ctrl-C | Interrupt the active confirmation or foreground Task command. |
Long prose wraps at word boundaries, while long Compose values wrap by
terminal cell width so the complete redacted plan remains inspectable. A ↳
marker identifies a visual Compose continuation and is not part of the source
document. Scroll is bounded to real content. Narrow terminals use short tab
labels and a focused action detail view; Page Up and Page Down reveal any
remaining details. Terminals smaller than 40 columns by 12 rows show only a
resize instruction instead of hiding controls or presenting a partial action.
The UI starts from local inspection only, so it opens offline and instantly.
For a real configuration it then re-reads the session about every twenty
seconds on its own: task status:json, and — when compute is present and the
config declares inference: — one bounded loopback read of the forwarded
Ollama. Automatic reads are quiet: they never move the screen, never claim the
UI is busy, and a read that fails never replaces what is on screen, because a
failed read is not evidence that anything changed. r remains available and
refreshes immediately. In synthetic mode the UI displays SYNTHETIC OFFLINE DEMO, performs no automatic reads, disables status refresh, and disables every
action except Preview and Validate.
The Actions screen lists only what the configuration can actually run:
data-up without a persist: block, seed without a seed: command, ci:
actions without a ci: block, and expiry registration without a session:
block are hidden rather than shown as refusals. For everything still listed it
explains why an operation is disabled before anything is delegated.
Control-plane bootstrap and tunnels are available only when their corresponding
declarative config exists. If a loaded local status proves compute is absent, bootstrap, tunnel,
shell, seed, and expiry registration remain disabled; without a status observation,
the UI labels them config-eligible and leaves runtime verification to the
Taskfile. Provider actions require an explicit chalupa.yml; down
additionally requires a fresh provider-bound status as described above.
Compact mode keeps the selected action, risk, availability, and exact
confirmation phrase in the focused view.
For an explicit config, the Status screen presents compute and protected data
separately. An unknown data state remains visible with its bounded reason;
the UI never collapses it into absent. A fresh present compute status with a
provider ID also enables provider-bound sinking for two minutes.
For confirmations and foreground Task output, the UI temporarily returns
terminal ownership to the command. A tunnel keeps ownership until its Task or
SSH process exits. After a completed Task, press Enter to return to the UI.
Starting a status refresh clears the previous observation immediately, and a
failed refresh leaves it unknown. Running data-up, up, or down also
invalidates the displayed observation, even when the Task fails, so a stale
provider identity cannot keep a lifecycle action enabled. The status view
shows the safe-down freshness deadline and repaints when it expires.
Recover from unknown state
If data-up, up, or down fails or is interrupted, do not immediately
repeat it or assume that rollback succeeded. Resource state and billing may be
unknown.
- Run
chalupa status --config "$DEMO_CONFIG"to inspect bounded local compute and protected-data state. - From the checkout, run
task status CONFIG="$DEMO_CONFIG"for the Taskfile's own human view and extra diagnostics. - Verify the exact resource in the DigitalOcean console and check billing.
- Re-run
chalupa inspect --config "$DEMO_CONFIG"before choosing the next lifecycle action.
An absent local stack is not proof that the provider has no resource. A
present protected-data state identifies bounded local outputs and the
configured expected size, not live provider capacity or billing. Do not use
data-nuke as recovery.
Taskfile and secret boundaries
The CLI passes a canonical config path to a fixed public Taskfile action. The Taskfile continues to own validation, the local Pulumi backend, tvault profiles, SSH, reporting, and separation of compute from persistent data. Do not call underscore-prefixed tasks or Pulumi lifecycle commands directly.
The CLI does not cache config history, persist secret values, or create a credential store. Tasks resolve their existing least-privilege tvault profiles only when the selected action needs them. The redacted preview and local Pulumi state can still contain private operational metadata; protect the workstation and state directory.
Continue with Quickstart, Lifecycle, Secrets with tvault, and Troubleshooting.
Remote inference
See Remote GPU inference for the experimental host-Ollama workflow.
chalupa exec --format env --config PATH prints endpoint exports without changing files.
See Agent harnesses for launch commands and configuration.
setup --inference links the hosted account and writes a GPU starter with console
heartbeats, then offers to arm the organization's console reaper so it can stop
reported GPU deployments even when the laptop sleeps. --no-reaper-token skips
arming. --no-console skips linking and omits cloud:, warning that expiry
depends on this machine staying awake and unlocked.
Context per inference session
Use chalupa launch opencode --context auto to negotiate context on
the existing remote GPU. --context 64k, 128k or native request a
specific size or the model ceiling. See Inference for
fit checks and limitations. The YAML and teardown identity are unchanged.
Managed telemetry bootstrap
chalupa up arms console telemetry after the managed host is ready. The success
line is Telemetry armed: heartbeats, GPU samples and inference runs will appear in the console.
Use chalupa session bootstrap [--config PATH] [--json] to retry delivery or
rotate the environment's ingest key. tunnel and launch retry automatically
when the managed record has bootstrapped: false (including older records without
that field). JSON reports bootstrap state and the public key ID, never key material.
See Managed GPU for signals and receipt limitations.