# fundaia
The command line for the [Fundaia](https://infrastructure.fundaia.com) deployment platform: projects,
services, variables, deployments, logs and metrics, from a terminal. It talks to any instance of the
platform — `--url` and profiles are first-class, so one binary reaches production, staging and a
laptop.
What the platform does, and every command grouped by topic, is in the product documentation:
[infrastructure.fundaia.com/docs/cli](https://infrastructure.fundaia.com/docs/cli). This README
covers installing, scripting, updating and working on the crate.
## Install
```bash
cargo install fundaia
```
The crate is public on crates.io; that is the route that works for everybody. Each `cli-v*` release
of the repository also carries prebuilt binaries for `x86_64-unknown-linux-gnu`,
`aarch64-unknown-linux-gnu`, `x86_64-apple-darwin` and `aarch64-apple-darwin`, but the repository is
private, so they are fetched authenticated:
```bash
gh release download --repo alex28042/fundaia-apps -p '*x86_64-unknown-linux-gnu.tar.gz' -D /tmp
tar -xzf /tmp/fundaia-cli-*.tar.gz -C ~/.local/bin
```
`cli/install.sh` downloads the same assets without credentials, so it answers 404 until the
repository is public. Nothing here uses `sudo` or edits a shell profile.
## Sign in
```bash
fundaia login --url https://infrastructure.fundaia.com
```
A terminal cannot present a passkey, so the credential is minted in the browser: the command prints a
short code, opens the approval page and waits. It works over SSH (`--no-browser` prints the address).
The token lives in `~/.config/fundaia/config.toml` (directory `0700`, file `0600`). `logout` only
forgets it locally; revoke it from **Línea de comandos** in the web interface.
```bash
fundaia status # every project, in one screen
fundaia new shop api --repo owner/api --connect-to Postgres
fundaia deploy shop api # deploy, and watch the six stages
fundaia logs shop api -f
fundaia attention # what is broken, everywhere, worst first
fundaia diagnose shop api # findings, each with the next step
fundaia grep shop ECONNREFUSED # a text in every service's log
fundaia fix deployment shop api # Claude Code fixes the newest failed deployment
```
Projects and services are named by id, slug or display name; an ambiguous name is an error, not a
guess.
## Scripting and AI agents
- `--json` (or `FUNDAIA_JSON=1`) prints one compact JSON value on stdout — the server's own DTOs.
`logs -f` and `watch` print one object per line; `deploy --json` waits and prints one result.
The diagnosis commands (`attention`, `health`, `diagnose`, `grep`, `deps`, `compare`, `probe`,
`security`…) print the same object the matching MCP tool returns.
- A failure is one object on stderr: `{"error":{"code","message","exit","retryable",…}}`, with the
server's error code. Colour is off in JSON mode.
- The exit code is the class, and the numbers never move:
| Exit | Meaning | Retryable |
|---|---|---|
| 0 | success | — |
| 1 | a bug in the CLI | yes |
| 2 | bad arguments (clap) | no |
| 3 | `not_found` | no |
| 4 | `validation_failed` | no |
| 5 | `unauthorized` — run `fundaia login` | no |
| 6 | `forbidden` | no |
| 7 | `conflict` | no |
| 8 | `rate_limited` | yes |
| 9 | `external_service_failed` | yes |
| 10 | the instance is unreachable | yes |
| 11 | the deployment failed | no |
- `fundaia schema --json` describes every command, argument and exit code, and whether a command
`reads`, `writes`, `starts`, `destroys` or is `local`. `fundaia guide` prints the platform's
briefing for agents (`AGENT.md`, the same truths the instance serves at `/agent.md`). Both work
offline.
- Completions: `fundaia completions bash|zsh|fish`.
### A Claude Code skill
`fundaia skill install` writes `~/.claude/skills/fundaia/SKILL.md` (`--project` writes it into
`.claude/skills/` of the current directory). It is generated from this binary's own commands and
the guide, so it lists exactly what the installed version can do; run it again after an upgrade.
`fundaia skill` prints it without writing anything.
## CI and profiles
Every global option reads an environment variable (`FUNDAIA_URL`, `FUNDAIA_TOKEN`, `FUNDAIA_PROFILE`):
```yaml
env:
FUNDAIA_URL: https://infrastructure.fundaia.com
FUNDAIA_TOKEN: ${{ secrets.FUNDAIA_TOKEN }}
run: fundaia deploy shop api
```
```bash
fundaia login --url https://staging.example.com --as-profile staging
fundaia --profile staging status
```
A saved token never travels to another host: `--url` pointing elsewhere than the profile needs
`--token` too. Plain `http://` is refused except on loopback (`FUNDAIA_INSECURE_HTTP=1` overrides).
## Updates
The tool updates itself. Once a day it asks crates.io for a newer release (alongside the command,
never before it), and installs it: the release binary when it can be downloaded — its `sha256` is
mandatory — and `cargo install` otherwise, which is what happens today because the assets are
private. The swap is a `rename`, so the running command finishes on the version it started with.
Notices go to stderr; nothing here can make a command that worked exit non-zero.
| Inside CI (`CI` set) | Notice only, never installs |
| `FUNDAIA_NO_SELF_UPDATE=1` | Notice only — your package manager owns the binary |
| `FUNDAIA_NO_UPDATE_CHECK=1` | No check at all |
| `fundaia upgrade [--check]` | Update now, ignoring the daily cache |
| `fundaia changelog [<version>\|--since <v>\|--all]` | What each version brought, compiled into the binary |
| `fundaia changelog --history` | Which versions have run on this machine (`~/.config/fundaia/versions.toml`) |
## Working on it
```bash
cd cli
cargo build --release
cargo test
cargo clippy --all-targets -- -D warnings
FUNDAIA_PERF_BUDGET_MS=150 cargo test --release -- --include-ignored # startup and hot-path budgets
```
`cli.yml` runs formatting, clippy and the tests on every change under `cli/`; the performance
budgets and the 8 MiB release-binary ceiling run on `main`, before anything is published.
A new command is finished when it has its `--json` shape and its row in `EFFECT_OF`
(`src/commands/schema.rs`); the platform side — service, REST route, MCP tool — is described in
`.claude/rules/surfaces.md`.
## Releasing
Nobody edits the version by hand. The repository's `post-commit` hook raises `Cargo.toml` from the
commit type when a commit touches `cli/` (`feat` minor, `!` or `BREAKING CHANGE` major, anything else
a patch) and stamps `CHANGELOG.md`, whose entries are written by a person.
A push to `main` that touches `cli/**` runs `.github/workflows/cli.yml`: if the tag
`cli-v<version>` does not exist yet, it tags, creates the GitHub release, builds the four binaries
with their checksums and publishes the crate. A version already on crates.io is skipped, so a run can
be repeated safely; `gh workflow run cli.yml --ref cli-v<version>` rebuilds a release from its tag.
Publishing uses `CARGO_REGISTRY_TOKEN`, scoped to `publish-new` and `publish-update` on this crate,
with no expiry. If it is missing the publish step says so and ends green; mint a replacement under
**Account settings → API tokens** on crates.io and add it to the repository under **Settings →
Secrets and variables → Actions**. When GitHub Actions cannot
run at all, `bun run ship` publishes the crate from a laptop (see `deploy/README.md`).
## Licence
MIT.