Skip to main content

flieber-cli

flieber is the command-line and agent interface to Flieber's commerce data. It gives developers, scripts and AI agents (Claude, Cursor, Lovable, custom agents) one stable, versioned way to read a tenant's:

  • actual sales: what each channel recorded, per product × store;
  • adjusted sales: actuals corrected for stockouts, outliers and price effects, used as the forecast input;
  • forecast: expected demand per product × store, from today on (meta.last_nonzero_forecast_period is the latest period with a non-zero forecast; core-service exposes no horizon).

Status: 0.1.4 on PyPI (2026-10-16). The architecture and contract v1 are ADR-0001 (Accepted). Work is tracked in the Linear project "Data Layer Product (Vibee) | Backend" (team Flieber AI - R&D). Which clients and modes are proven: docs/clients/.

Shape

One Python package: a shared core, and several surfaces built on it.

            ┌──────────────────── flieber (Python package) ────────────────────┐
            │  core: auth · GraphQL client · versioned tool contract · shaping  │
            └──────┬──────────────────────┬───────────────────────┬────────────┘
                   │                      │                       │
          CLI  `flieber …`      MCP (local)  `flieber mcp`   MCP (hosted)  `flieber mcp --http`
          humans & scripts      stdio, launched by           container behind a stable URL,
                                Claude / Cursor              used by the self-serve product
  • Core
    • Authentication with a Flieber public-API key (FLIEBER_API_KEY, or the OS keyring or a 0600 config file, written by flieber auth login).
    • Sign-in with a Flieber account (flieber login): Clerk OAuth, then core-service REST with the access token and GraphQL with a short-lived exchanged JWT (ADR-0004). The API-key route stays available behind FLIEBER_API_KEY_ROUTE (default on).
    • A client for core-service's public GraphQL API.
    • The versioned tool contract: one definition per capability. It drives both the CLI command and the MCP tool, so the two cannot drift (a ratchet test checks the parity, ADR-0001 §7).
    • Output shaping for humans (tables) and for LLMs (compact {columns, rows}, point caps, pagination).
  • CLI: flieber status, flieber listings, flieber products, flieber mapping, flieber sales, flieber adjusted, flieber forecast, flieber trends, and flieber onboard … (below). JSON output is available for scripts. flieber skills install puts the agent skills (skills/, shipped in the package from 0.1.3) where Claude Code or Cursor reads them (docs/clients).
  • MCP: the same capabilities as MCP tools, over stdio (local) or streamable HTTP (hosted, stateless). The hosted mode forwards the caller's own API key, or accepts a Flieber sign-in (a Clerk OAuth token it verifies with Clerk, for a claude.ai connector), and stores nothing.

Design rule: customers bind to this contract, never to the raw GraphQL schema. The backend behind it may change; the contract changes only through versioning.

Signing in

--env is global: flieber --env qa login, not flieber login --env qa. prod is the default.

Two credentials, kept separate:

  • API key. flieber auth login stores <system_user_id>:<secret>. FLIEBER_API_KEY overrides the stored key. flieber auth logout removes it. flieber auth status says where the key comes from, whether an account is signed in, and which of the two commands use (in use:). It never prints a key or a token.
  • Flieber account. flieber login prints the sign-in URL, then tries to open a browser, and says in one line when it couldn't (--no-browser prints the URL only). On WSL it uses BROWSER when set, else wslview (wslu) when installed, else Windows' rundll32 (the URL opens in the Windows browser), and never gio, xdg-open or cmd.exe; on other Linux it needs a display. It stores a Clerk OAuth token set under oauth:<env>:<profile>, so a QA login is never sent to production. flieber logout forgets it. GraphQL reads then use a JWT exchanged from that login, cached for the organisation it was exchanged for. Writes over this login stay off (FLIEBER_OAUTH_WRITES is off unless it is 1 or true).

FLIEBER_API_KEY_ROUTE=off turns the API-key route off without a code change. The default is on. On the hosted MCP this is a Railway variable, read when the process starts. While it is off, the hosted /mcp route refuses every API key; a Flieber sign-in (OAuth) still works, and /healthz stays open. The CLI asks you to flieber login instead. While it is on, a key from FLIEBER_API_KEY or the store wins GraphQL reads over a sign-in (ADR-0004 §3): flieber login warns when that is the case, and an error about the key says that a sign-in is also present.

The flieber CLI Clerk app is a public PKCE client. QA's client id is OrZunJwBXl8yTnzX; production's is oQmB5XYQbPbW9uZ8. FLIEBER_OAUTH_CLIENT_ID overrides either. Login listens on 127.0.0.1:8765 (Clerk requires that exact redirect). If that port is busy, login stops and names it. The tests here do not sign in against QA or production. core-service accepting this client is AIDEV-647 and is not proved by this package; production does not accept it until that allow-list is on.

Which organisation

--tenant is global too (flieber --tenant org_… <command>, or FLIEBER_TENANT). It overrides the organisation stored at login for that call. flieber login --tenant org_… stores that organisation and beats FLIEBER_TENANT; the global --tenant still wins. The value is a tenant UUID or a Clerk organisation id (org_…).

On MCP (stdio and hosted) every tool takes an optional tenant argument instead (AIDEV-572). It wins over --tenant, FLIEBER_TENANT and the stored organisation for that one call, and a preview made in one organisation cannot be committed in another. On the CLI the global --tenant fills that argument; there is no per-command --tenant. An API key reads only its own tenant: with a key, tenant (or the global --tenant, including flieber --tenant X mcp and flieber --tenant X auth login) is refused with invalid_input rather than ignored. FLIEBER_TENANT is ignored with a key, because it is often set globally.

The portal's organisation switcher shows the organisation's name, not its id. The id is under the user menu → Manage organization. A person who belongs to one organisation can omit --tenant. A person who belongs to several, and does not pass one, gets invalid_input telling them to (on MCP: to pass the tenant argument). flieber tenants (the tenants tool on MCP) lists the organisations your sign-in belongs to, with the ids to pass as tenant; it needs core-service's AIDEV-656 endpoint, and an API key cannot use it.

Environment variables

Variable Meaning
FLIEBER_ENV prod (default) or qa.
FLIEBER_API_URL GraphQL URL override.
FLIEBER_REST_URL REST base override. Defaults: https://app.flieber.com/api/v1, https://app.flieberqa.com/api/v1.
FLIEBER_API_KEY Public-API key. Ignored when FLIEBER_API_KEY_ROUTE=off.
FLIEBER_API_KEY_ROUTE on (default) or off.
FLIEBER_OAUTH_CLIENT_ID Clerk public client id. Defaults: QA OrZunJwBXl8yTnzX, production oQmB5XYQbPbW9uZ8.
FLIEBER_OAUTH_ISSUER Clerk Frontend API origin. Defaults: QA https://one-pug-9.clerk.accounts.dev, production https://clerk.flieber.com.
FLIEBER_OAUTH_SCOPES Default profile email offline_access. openid is not requested.
FLIEBER_OAUTH_REDIRECT_PORT Loopback port. Default 8765 (the port Clerk has registered). 0 is rejected.
FLIEBER_OAUTH_WRITES 1 or true enables writes over OAuth. Default off. on does not enable it.
FLIEBER_TENANT Tenant UUID or org_…. The global --tenant flag wins.
FLIEBER_PROFILE Credential profile. Default default.
FLIEBER_ONBOARDING_MODE mcp (default) or rest. Which onboarding adapter flieber login uses.
FLIEBER_ONBOARDING_URL Onboarding tunnel URL. Defaults to the Railway doors for prod and qa.
FLIEBER_ONBOARDING_TIMEOUT_S Tunnel timeout in seconds. Default 190. A value below 190 is rejected.
FLIEBER_CLERK_SECRET_KEY Hosted server only (flieber mcp --http): the Clerk secret key it verifies OAuth bearers (oat_…) with. Never printed or logged. Unset: an oat_ bearer is refused with 503.
FLIEBER_HOSTED_* Hosted server only: the gate in front of Clerk. See Hosted server: the gate in front of Clerk.
FLIEBER_PUBLIC_URL Hosted server only: its https origin (https://host[:port], no path), for the OAuth discovery documents and the 401 challenge. Defaults to https://$RAILWAY_PUBLIC_DOMAIN; unset with neither: no discovery. flieber mcp --http refuses any other value; no other command reads it.

Onboarding uses the flieber login session. When an API key and a session are both present, GraphQL stays on the key and the key's tenant; onboarding and the write checks use the session's tenant. REST mode refuses a tenant with more than 1000 stores, locations or store settings; use --mode mcp for that tenant.

Hosted server: the gate in front of Clerk

flieber mcp --http verifies every Flieber sign-in (oat_…) with Clerk's Backend API, under a secret shared with the ai-lab agent tunnel. In front of that call stands a gate, in memory, per process (one replica per environment, ADR-0002), built by flieber.app.build_hosted_gate (ADR-0004, "Production gate"). In order, for an oat_ bearer:

  1. Verification cache. A token Clerk verified is remembered by its SHA-256 (never the token) for the TTL, and never past the expiry Clerk reports for it. A token Clerk rejected (auth_invalid) is answered 401 from memory for the negative TTL. A Clerk outage (503) is never remembered. Neither answer spends a bucket.
  2. Single flight. Concurrent requests for the same new token wait for one Clerk call and share its answer.
  3. Per-client bucket, then global bucket. Each Clerk call takes one token from the client's bucket and one from the process-wide bucket. An empty bucket is 429 rate_limited with Retry-After and retry_after_s, and Clerk is not called.

API keys (<system_user_id>:<secret>) never pass through the gate; they keep the client-side bucket per key. A revoked token keeps working for up to the cache TTL. Every value is read when the process starts; a bad value stops flieber mcp --http with invalid_input.

Variable Default Allowed Meaning
FLIEBER_HOSTED_VERIFY_CACHE_TTL_S 60 0–300 Seconds a verified token is trusted without asking Clerk. 0 turns the positive cache off. Also the longest a revoked token keeps working.
FLIEBER_HOSTED_VERIFY_CACHE_SIZE 10000 1–1000000 Tokens remembered (each of the positive and negative caches); the least recently used goes first.
FLIEBER_HOSTED_VERIFY_NEGATIVE_TTL_S 5 0–60 Seconds a rejected token is answered 401 without asking Clerk. 0 turns it off.
FLIEBER_HOSTED_VERIFY_PER_CLIENT_PER_MINUTE 120 1–6000 Clerk calls a minute per client address (burst of the same size). An IPv6 client counts as its /64. 120, not lower: a claude.ai connector calls from Anthropic's servers, so one address carries many users' connectors (each costing one Clerk call a minute at most, with the 60 s cache). Watch limited lines with bucket client.
FLIEBER_HOSTED_VERIFY_GLOBAL_PER_SECOND 20 1–1000 Clerk calls a second for the whole process (burst of the same size).
FLIEBER_HOSTED_CLIENT_IP_HEADER X-Real-IP a header name, or none The header that carries the client's address. Its right-most value is used; without it, or with a value that is not an IP address, the TCP peer is. none uses the TCP peer only.

The client address on Railway. Railway's edge sets X-Real-IP to the client's address (specs) and overwrites any copy the client sends, together with X-Forwarded-For (Railway staff, September 2026). X-Forwarded-For has been observed to arrive as client, CDN hop: its right-most entry is a shared edge address, so it is not used by default. The TCP peer is Railway's proxy (a 100.x address shared by everyone), so without the header every client would share one bucket. Behind any other proxy, set the header that proxy sets and strips; with no proxy, set none.

Logs. The hosted server writes every record as one JSON line on stdout, with a level and a message (deploys, logs and rollback: docs/hosted-mcp-runbook.md). Each gate decision is one oauth_verify_gate line with outcome, the token's 8-character fingerprint (key_sha8) and the Clerk subject. Never the token, and never the client's address. Answers from memory (cache_hit, rejected_cached, joined) are DEBUG, so they do not appear at the hosted server's INFO level; verified, rejected and limited are INFO; unavailable (Clerk could not decide) is WARNING. limited adds bucket (client or global) and retry_after_s, and is sampled: under a flood, at most one line per bucket key every 5 seconds, the next one carrying suppressed (how many it held back).

Onboarding (flieber onboard, AIDEV-644)

flieber onboard … onboards the signed-in user's selected tenant: connect stores, map SKUs, set up inventory locations, link stores to locations, and finish setup (ADR-0003). The same operations are MCP tools named onboard_*.

Not yet proven against QA. These commands need a Flieber sign-in (flieber login, above). Without one they answer auth_missing. Writes over that sign-in stay off until FLIEBER_OAUTH_WRITES is set (writes_disabled). No mode has a recorded QA run yet (invariants §5).

Command What it does
flieber onboard status The tenant's onboarding state as the backend reports it: live or not, whether finishing is allowed, what blocks it.
flieber onboard connect <provider> amazon, ebay, etsy: prints and opens the consent URL (--no-browser prints only), then waits for the new connection to finish importing (--no-wait, --timeout SECONDS, default 600; a timeout exits 5). Amazon takes --region and --account-type. walmart, bigcommerce and shopify print where to connect instead; flieber never takes their credentials.
flieber onboard connections The connections and their import state.
flieber onboard stores list|create|link List stores; create one; set a store's fulfillment and storage locations (--clear-…-location clears one; an option left out keeps it).
flieber onboard skus list|map List a store's SKUs (--page); map SKUs to products from --file (a CSV whose headers are sku,product_code,product_name[,is_archived,thumbnail_url]) or repeated --row.
flieber onboard locations list|create|update Inventory locations (--link CONNECTION_ID:EXTERNAL_LOCATION_ID).
flieber onboard finish Finish setup. Irreversible: it sets the tenant live. You confirm by typing the tenant's name; there is no --yes.

Every write previews first. The command shows the preview, asks once, then commits exactly what was previewed. --yes answers the question for scripts; it does not skip the preview, and with --preview-id (the pc_… id printed after the preview the person confirmed) it commits only if the new preview is that same one, otherwise refusing with preview_mismatch. Without a terminal and without --yes, the preview is the output and nothing is applied (exit 0). A declined prompt also exits 0 ("Nothing applied."). Messages and prompts go to stderr; stdout holds one parseable result.

Large SKU files. skus map first checks the CSV header against the row fields: an unknown, missing or duplicate column is refused with invalid_input that names it, and nothing is sent. A column with a blank header and only blank cells (a spreadsheet's trailing comma) is ignored; a blank header over data is refused by its column number. It then previews the file in batches of at most 500 rows. Rows the backend would reject are listed by file row (1 = the first row after the header) and left out; the remaining rows are previewed again, in at most 3 passes (if the third pass still rejects new rows, it stops with invalid_input, listing every rejected row in rows_rejected, and commits nothing). The command asks once, then commits batch by batch. At the first batch that fails, or whose outcome is unknown, it stops and says which file rows were applied, rejected, unknown or not applied, and not attempted. A Ctrl-C during the commits gives the same report and exits 130: a batch being committed is unknown; between batches, none is. Run it again with --from-row N to resume. Committing a row again is safe: the mapping is an upsert. Each distinct warning from the backend is printed once per command.

The stop report, for scripts. With --format json, a failed batch is the error object (a non-zero exit, from its code), and a Ctrl-C is a document {"interrupted": {…}} (exit 130). Both carry applied (of the batch that failed or was in flight: false or "unknown"; false for a Ctrl-C between batches), next (the read to run first when "unknown"), and these fields:

Field Value
rows_applied, rows_rejected, rows_not_attempted File rows, as a string
rows_unknown (outcome unknown, and every Ctrl-C) or rows_not_applied (the batch failed) The failed or in-flight batch's file rows, as a string ("" for a Ctrl-C between batches); exactly one of the two is present
resume_from_row An integer: the value for --from-row

The row strings are ascending, comma-and-space separated, with inclusive ranges for consecutive rows: "1, 3, 5-502". An empty string means no rows. To get integers, split on ", " and expand each a-b.

--mode mcp|rest (on flieber onboard, default FLIEBER_ONBOARDING_MODE, else mcp) picks the backend: ai-lab's onboarding tools (MCP), or core-service's REST endpoints. In MCP mode the onboarding tools hold the step logic; REST mode cannot tell readiness for a tenant with the Flieber app, and says so.

FLIEBER_OAUTH_WRITES=1 enables writes over a sign-in, onboarding writes included. It is off by default, because core-service's token exchange still grants every write to OAuth users (AIDEV-646); until then a write over OAuth answers writes_disabled.

Principles and how to contribute

  • What the project is, and the rules no change may break (the three signals, least privilege, no secrets at rest, bounded cost and output, honest results): docs/00-invariants.md. It ranks above everything else.
  • How work is done (proof before done, test-first, ports and adapters, one authoritative path per process): AGENTS.md. It applies to humans and agents alike.
  • Decisions: docs/adr/.
  • The capabilities (inputs, columns, grants, budgets, writes, errors, the three-signal rules, limits): docs/tools.md, generated from the registry; flieber docs prints it.
  • Clients (Claude Code, Cursor, claude.ai, the hosted URLs): configuration snippets and which are proven, in docs/clients/.

Tooling (v0)

  • Python ≥ 3.12.
  • Packaging with uv and pyproject.toml; installable as uvx flieber … / pipx install flieber.
  • Tests with pytest, run as uv run pytest (ADR-0001 §8).

flieber is published on PyPI: 0.1.4 is the latest release (tag v0.1.4, 2026-10-16). Releases, the version scheme and the changelog rules: docs/release.md.

Development

uv sync                                        # Python ≥ 3.12, with the dev dependency group
uv run pytest                                  # tests never reach the network
uv run ruff check src tests scripts && uv run ruff format --check src tests scripts
uv run python -m flieber.contract.document --write   # after a contract change; review the diff
uv run python -m flieber.contract.docs --write       # regenerate docs/tools.md and the agent instructions
python scripts/pin_schema.py <core-service ref>      # re-pin core-service's schema
scripts/release_check.sh                       # build and verify dist/ exactly as CI does (docs/release.md)
uv run flieber --env qa ping                   # the proof run against QA (needs a QA key)

How work is done here, including what counts as proof, is in AGENTS.md.

  • core-service: the public GraphQL API at /api/graphql, and public-API keys with per-resource grants.
  • flieber-build-front: the self-serve portal, which issues API keys and shows the MCP and CLI configuration.
  • ai-lab tools/flieber_agent_tunnel: the existing ask_flieber MCP server. It is the reference for the streamable-HTTP MCP skeleton and the Clerk OAuth pieces.

Metadata

Release files for flieber 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flieber 0.1.4
File Size Uploaded Provenance
flieber-0.1.4.tar.gz 226.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for flieber 0.1.4
File Interpreter ABI Platform
flieber-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 479.4 kB

Release files / flieber-0.1.4.tar.gz

Download URL flieber-0.1.4.tar.gz
Size 226.5 kB
Tags Source
SHA-256 checksum
How to use checksums
0ba0d8ee8e831e1aafac8c27b4260505583af2fbe5cad113befb822bcc7f4c0f
BLAKE2b-256 checksum
How to use checksums
d3e37dbe9221cc92a39ec82e227708fd8908709c561956817676b788c8e3ca04
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / flieber-0.1.4-py3-none-any.whl

Download URL flieber-0.1.4-py3-none-any.whl
Size 252.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
09fcb5d7b9da67ad4908b27652cb32f631f01b15fd4783297da002805ea7b3bc
BLAKE2b-256 checksum
How to use checksums
b48b4d29d573cbd28de3940c86bbbe945e55e67fb52b1d7b974b952d89e5cc75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.12.15

Security for flieber 0.1.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page