Metadata-Version: 2.4
Name: movitera-cli
Version: 0.6.0
Summary: Movitera CLI: inject vault secrets as environment variables, generate TOTP codes
Project-URL: Homepage, https://github.com/joaoheusi/movitera-cli
Project-URL: Repository, https://github.com/joaoheusi/movitera-cli
Project-URL: Issues, https://github.com/joaoheusi/movitera-cli/issues
Author: Movitera
License-Expression: MIT
License-File: LICENSE
Keywords: cli,dotenv,movitera,password-manager,secrets,totp,vault
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Requires-Python: <4,>=3.11
Requires-Dist: httpx<1,>=0.28.1
Requires-Dist: keyring<26,>=25
Requires-Dist: pyotp<3,>=2.9
Requires-Dist: rich<15,>=13
Requires-Dist: textual<9,>=8
Requires-Dist: typer<1,>=0.15
Description-Content-Type: text/markdown

# movitera CLI

Inject Movitera vault secrets as environment variables, browse credentials, render dotenv files, and generate TOTP codes from the terminal. Authenticates with a Movitera vault personal access token (PAT).

Secrets are addressed either as a **project environment** (`-p myapp -e production`) — per-key, versioned, and writable — or as an **`ENV_BUNDLE` credential** (`-c myapp-prod`). Both models work everywhere and neither is going away.

## Install

```bash
pip install movitera-cli
# or install as an isolated CLI tool (recommended):
pipx install movitera-cli
# or with uv:
uv tool install movitera-cli
# or run without installing:
uvx --from movitera-cli movitera --help
```

## Quick start

```bash
# 1. Sign in (opens your browser, click "Authorize" — no copy/paste).
#    Your team is auto-saved as the default.
movitera login

# 2. See what you can reach:
movitera whoami
movitera project list
movitera creds list

# 3. Bind this repo to a project + environment (writes movitera.yaml):
movitera setup

# 4. Run any command with that environment injected — no flags needed:
movitera run -- npm start

# 5. Or dump it as dotenv (stdout, or a 0600 file with -o):
movitera secrets pull -o .env

# 6. Or generate a TOTP code:
movitera totp <credential-id>
```

Omit any of `--project`, `--env`, `--credential`, or `--team` on a terminal and the CLI shows an interactive picker.

## Two ways to address secrets

| | Project environment | Env-bundle credential |
|---|---|---|
| Address | `-p myapp -e production` | `-c myapp-prod` |
| Granularity | one key at a time | one blob |
| History | per-value versions + rollback | — |
| Writable from the CLI | yes (`secrets set` / `upload`) | no |
| Export formats | `env`, `json`, `yaml`, `docker` | dotenv |

`movitera run` picks the project model when a project is named by a flag, an
environment variable, or a `movitera.yaml`; an explicit `-c` always wins, and
with no signal at all it behaves exactly as it did before projects existed.
Mixing `-c` with `-p`/`-e` in one invocation is an error rather than a guess.

### Repo binding (`movitera.yaml`)

`movitera setup` writes the address into a `movitera.yaml` at the repo root:

```yaml
# Movitera project binding for this repository.
# Addresses only — never secrets. Safe to commit.
team: 665f…
project: myapp
env: development
```

Commit it, and every teammate's `movitera run -- npm start` resolves without
flags. It's found by walking up from the working directory, so it also works
from subdirectories. Addresses only — no secret ever goes in this file.

## Interactive mode

Run `movitera` with no arguments on a terminal (or `movitera tui`) to open a
full-screen UI: a teams sidebar on the left, and on the right a search box over
your credentials **grouped by scope** (Privadas first, then each group, mirroring
the web app), filtering as you type. Keyboard-driven:

| Key | Action |
|---|---|
| `↑`/`↓` | Move between teams / credential rows |
| `/` | Jump to the search box (filters the current team's credentials) |
| `t` | Focus the teams list · `u` set the highlighted team as default |
| `Enter` | Open the action menu for the selected credential |
| `r` | Refresh · `q` quit |

Selecting an `ENV_BUNDLE` offers **copy to clipboard** or **write to a 0600
file**; other credentials offer a live **TOTP code** with a countdown. As in the
one-off commands, secret *values* are never rendered on screen.

This is purely additive — every one-off command below is unchanged, and a bare
`movitera` that isn't attached to a TTY (pipes, CI) prints help instead of
launching the UI, so scripts never block. The UI covers credentials; project
environments are one-off commands for now.

## How it works

`movitera run -- cmd` fetches a dotenv body — from
`GET /vault/projects/{id}/download` for a project environment, or
`GET /vault/credentials/by-name/{name}/env` for an env bundle — then `exec`s the
child process with those vars merged into its environment. The CLI never writes
secrets to disk — they exist only in the child process's `environ` (and
`secrets pull -o` / `secrets get -o` write a `0600` file when you explicitly ask
for one).

A single PAT works across every team you belong to: the token is minted against
one team but credential reads authorize by your team membership, so you can
`movitera team use <name>` to switch the default at any time.

For CI, mint a token **scoped to one project × environment** in the web app
(Vault → project → settings) and pass it as `MOVITERA_TOKEN`. A scoped token
reaches only its own project's routes; using it with `-c` fails with a message
saying so.

Tokens are stored in the OS keyring (Keychain on macOS, libsecret on Linux,
Credential Manager on Windows) with a file fallback under
`~/.config/movitera/token` (mode `0o600`) if no keyring backend is available.
Non-secret preferences (default team, API URL) live in
`~/.config/movitera/config.json`.

## Config

Environment variables override both the repo's `movitera.yaml` and the saved
config file.

| Env var | Purpose | Default |
|---|---|---|
| `MOVITERA_API_URL` | API base URL | `https://api.movitera.com` |
| `MOVITERA_TOKEN` | PAT override (skips keyring lookup) | — |
| `MOVITERA_TEAM` | Default team id | — |
| `MOVITERA_PROJECT` | Default project slug | — |
| `MOVITERA_ENV` | Default environment slug | — |
| `MOVITERA_CONFIG_DIR` | Base config dir (like `XDG_CONFIG_HOME`); the token + config live in `<dir>/movitera/` | `~/.config` |

Precedence, highest first:

- **team** — `--team` → `MOVITERA_TEAM` → `movitera.yaml` → saved default → picker
- **project / environment** — `--project`/`--env` → `MOVITERA_PROJECT`/`MOVITERA_ENV` → `movitera.yaml` → picker

Run `movitera config` to see the resolved paths, values, and repo binding.

## Commands

### `movitera` / `movitera tui`
Opens the interactive UI (see [Interactive mode](#interactive-mode)). On a
non-TTY, bare `movitera` prints help instead.

### `movitera login`
Opens your default browser to the Movitera approval screen. Click "Authorize"
and the CLI captures a freshly minted PAT via a one-time grant code
(PKCE-protected), stores it in the OS keyring, and pins the approved team as
your default. The PAT never appears in the URL or browser history.

Headless escape hatches — for CI, SSH, and Docker where no browser is available:

```bash
# Paste a PAT directly:
movitera login --token mvt_pat_…

# Pipe a PAT from another tool (`pass`, `vault`, etc.):
pass show movitera/pat | movitera login --stdin

# Or skip `login` entirely by setting MOVITERA_TOKEN at runtime:
export MOVITERA_TOKEN=mvt_pat_…
movitera secrets pull --team <teamId> -c myapp-prod
```

### `movitera logout`
Removes the stored PAT.

### `movitera whoami [--json]`
Shows the API URL, auth source, default team, and a live connectivity check.
Use `--json` for a machine-readable status object:

```bash
movitera whoami --json
```

### `movitera config`
Shows the resolved config path, API URL, and default team. Use
`movitera config set-url <URL>` to persist a different API base URL, or
`movitera config clear-url` to return to the default. API URL precedence is
`MOVITERA_API_URL` > saved URL > `https://api.movitera.com`.

### `movitera team list [--json]` / `team use <name>` / `team show` / `team clear`
List the teams you belong to, pin a default (by name, slug, or id) so other
commands don't need `--team`, show the current default, or forget it.
Use JSON when discovering team ids in scripts:

```bash
movitera team list --json
```

### `movitera creds list [--team T] [--env] [--search Q] [--json]`
Browse credential metadata (names, kinds, tags) in a team. `--env` limits the
list to `ENV_BUNDLE` credentials; values are never shown. Use `--json` to emit
a machine-readable array.

### `movitera project list [--team T] [--json]`
Lists the projects you can reach and each one's environments.

### `movitera setup [-p SLUG] [-e SLUG] [--team T] [--force]`
Writes the repo's `movitera.yaml` binding. Omit the flags on a terminal to pick
interactively; `--force` replaces an existing binding without asking.

### `movitera run [-p SLUG -e SLUG | -c NAME] [--team T] -- <cmd> [args...]`
Fetches the environment (or `ENV_BUNDLE` credential) and execs `<cmd>` with
those vars in the environment.

```bash
movitera run -p myapp -e production -- npm start
movitera run -- npm start          # address from movitera.yaml
movitera run -c myapp-prod -- npm start
```

### `movitera secrets pull [-p SLUG -e SLUG | -c NAME] [--format F] [-o FILE]`
Writes the export to stdout, or to `FILE` (mode `0600`) with `-o`. Exits
non-zero on resolution failures so you can safely chain `... > .env`.
`--format` takes `env` (default), `json`, `yaml`, or `docker`, and applies to
project environments only — an env bundle is dotenv by definition. A `docker`
export is refused when any value contains a newline, because that format cannot
represent one; the error names the offending keys.

### `movitera secrets list [-p SLUG -e SLUG] [--json]`
Lists the keys in an environment with their version and last-updated time.
Metadata only — values never appear.

### `movitera secrets get KEY [-p SLUG -e SLUG] [-o FILE]`
Prints one value to stdout (an audited read), or writes it to a `0600` file
with `-o`.

### `movitera secrets set KEY[=VALUE] [-p SLUG -e SLUG] [--comment C]`
Writes one key. Pass a bare `KEY` to read the value from stdin — or, on a
terminal, from a no-echo prompt — so it stays out of your shell history:

```bash
movitera secrets set API_KEY=abc123
openssl rand -hex 32 | movitera secrets set SESSION_SECRET
```

### `movitera secrets upload FILE [-p SLUG -e SLUG] [--yes]`
Merges a dotenv file into an environment. It first shows the server's
`new/updated/unchanged` diff and asks for confirmation; `--yes` skips the
prompt (and is required off a terminal). **Keys absent from the file are never
deleted**, and re-uploading an unchanged file writes nothing — so this is safe
to run on every CI build. Files above the server's per-request key cap are sent
in batches.

### `movitera secrets history KEY [-p SLUG -e SLUG] [--limit N] [--json]`
Shows a key's versions, newest first — 20 by default, up to 100 with
`--limit`. Version numbers are monotonic but neither contiguous nor 1-based,
so always roll back to a number this command listed.

### `movitera secrets rollback KEY --version N [-p SLUG -e SLUG]`
Restores a key to an earlier version. Nothing is lost: the restore is appended
as a new version, so a rollback can itself be rolled back.

### `movitera totp [CREDENTIAL] [--team T]`
Prints the current TOTP code for the credential's `OTPAUTH_URI` field. Omit the
credential on a terminal to pick interactively. Pass an id directly, or pass
an exact name when `--team`, `MOVITERA_TEAM`, or a saved default team identifies
where to resolve it.

### Shell completion
`movitera --install-completion` installs completion for your shell;
`movitera --show-completion` prints it.

> PAT management (creating/revoking tokens) and project management (creating
> projects and environments, grants, promotion) live in the Movitera web app —
> those endpoints require an interactive session, not a PAT.

## Security model

- PATs have full 256-bit entropy and are stored as SHA-256 on the server, so a
  server compromise cannot recover the plaintext.
- Every `movitera run` invocation is audited server-side, but PAT reads are
  **coalesced per hour per token** (see the backend's `FETCHED_VIA_TOKEN`
  aggregation) so a hot dev loop doesn't drown the log.
- Group/team access is re-checked on every request: if your vault scope is
  revoked, your PAT immediately loses access — no token-cache divergence.
