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: v0 in design. The architecture and contract v1 are ADR-0001 (Accepted). The code arrives through the issues in the Linear project "Data Layer Product (Vibee) | Backend" (team Flieber AI - R&D, milestone M3). Only the 0.0.1 placeholder is published so far.

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.
  • 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 and whether an account is signed in. It never prints a key or a token.
  • Flieber account. flieber login opens a browser (or prints a URL, for an SSH session) and 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.

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. 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. 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 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. 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. Run it again with --from-row N to resume. Committing a row again is safe: the mapping is an upsert.

--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/.

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 (the 0.0.1 placeholder, 2026-09-29, CI pipeline 2894945075). 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
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.2

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.2
File Size Uploaded Provenance
flieber-0.1.2.tar.gz 122.5 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 274.4 kB

Release files / flieber-0.1.2.tar.gz

Download URL flieber-0.1.2.tar.gz
Size 122.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a3767a937115ca28b6ab599ec9991091b4f64741f4bbb21442e98b40e2fe68a1
BLAKE2b-256 checksum
How to use checksums
6207b5f6068133544987571c29d2bf51181ec70b69b328cbd4e55a79466ac9c3
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.2-py3-none-any.whl

Download URL flieber-0.1.2-py3-none-any.whl
Size 151.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc640bb91152b3d5ecc14beabd644fc25b9c108d0eb90e27b2c1a01a007cdbd6
BLAKE2b-256 checksum
How to use checksums
ff6fece6d5b5be3324e2f764b6f569e6e51b88b60962d35847588bae3be43862
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.2

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