Skip to main content

workspaceguard-cli (Python)

Per-workspace usage metering and quota caps for one shared self-hosted AI assistant deployment -- a genuine, independent Python port of the workspaceguard-cli npm package, not a wrapper around a Node binary.

PyPI version License: MIT Python versions

Why this exists

Run Odysseus (or a compatible self-hosted assistant) for your whole household or small team, and there is no way to see who sent how many messages this month, or to stop one person's usage from burning through everyone else's API budget. WorkspaceGuard is a thin sidecar that adds that layer: per-workspace message counts, optional monthly caps that fail closed, and a CLI report an admin (or another agent) can read.

This project originally set out to add per-user workspace isolation (separate chat history, memory, API keys) to a self-hosted AI chat platform. A feasibility spike found that Odysseus already enforces per-user ownership on chat history, memory, and API tokens by default, so building a competing isolation layer would have duplicated work Odysseus already does correctly. This project instead keeps its tested isolation engine (namespace separation, an AES-256-GCM vault with real key rotation, fail-closed identity resolution, a self-healing circuit breaker) as the identity-resolution substrate, and builds the layer Odysseus does not provide: usage metering and quota enforcement per workspace. See the project README for the full story.

Install

pip install workspaceguard-cli

Current status: this Python port is fully built, tested (50/50 pytest tests passing), and published to PyPI. pip install workspaceguard-cli works today -- see pypi.org/project/workspaceguard-cli. The npm package (workspaceguard-cli) is also published and installable today via npm install -g workspaceguard-cli -- see npmjs.com/package/workspaceguard-cli. This package is a genuine, independent port -- not a wrapper around the npm package -- so it works and is maintained regardless of the npm package's status.

Quickstart

# Register the workspaces sharing one deployment (identity = the header
# value your reverse proxy sets after authenticating, e.g. Cloudflare
# Access).
workspaceguard add-workspace alex --identity alex@example.com
workspaceguard add-workspace jordan --identity jordan@example.com

# Optional: cap alex at 1000 messages/month. Omit for unlimited (the default).
workspaceguard set-cap alex 1000

# See usage for every workspace.
workspaceguard usage
$ workspaceguard usage --json
{"ok": true, "usage": [{"workspaceId": "alex", "identity": "alex@example.com", "monthlyMessageCap": 1000, "percentUsed": 0, "period": "2026-07", "messageCount": 0, "estimatedBytes": 0}]}

CLI reference

Every command accepts --json for a structured, agent-native output shape instead of the human-readable text shown below. Identical command surface to the npm CLI.

Command What it does
workspaceguard init Initializes the data directory and vault for this deployment.
workspaceguard add-workspace <id> --identity <value> Registers a workspace, idempotent on repeat calls for the same id.
workspaceguard status [--json] Lists configured workspaces.
workspaceguard usage [--json] Per-workspace message count, cap, and percent-used for the current month.
workspaceguard set-cap <id> <count|none> Sets or clears a workspace's monthly message cap.
workspaceguard rotate-key <id> Rotates a workspace's vault encryption key (invalidates the old ciphertext).
workspaceguard scan [--json] Isolation config scan (scaffold stub, carried over from the original build).
workspaceguard -h, --help Prints the command list above and exits.
workspaceguard -V, --version Prints the installed package version and exits.

Global options

Option What it does
--data-dir <path> Data directory for config, vault, and usage data. Takes precedence over WORKSPACEGUARD_DATA_DIR.
--force init only: regenerate the master key even if an existing key file at the resolved data dir looks corrupted or truncated. Warning: permanently invalidates anything already encrypted under the old key.
--json Structured, agent-native output instead of human-readable text.

Data directory resolution, in order: --data-dir flag, then WORKSPACEGUARD_DATA_DIR env var, then ~/.workspaceguard. This used to default to the current working directory with no override -- running init from the wrong shell could silently write a live encryption key into an unrelated directory. init on an existing, valid key is idempotent (it loads and reuses that key); init on a key file that exists but doesn't decode to a valid key refuses to overwrite it without --force.

Known issue on the currently published 0.1.2 release: the -h/--help and -V/--version rows above describe the fixed, not-yet-published behavior. workspaceguard-cli==0.1.2 (the version live on PyPI today) does not implement those two flags; running either one falls through to the default unknown-command path and exits with status 1. Every other command in this table works as documented on 0.1.2. See CHANGELOG.md for the fix and its publish status.

Library API

import asyncio
from workspaceguard import create_workspace_guard, MockAdapter, QuotaExceededError

async def main():
    guard = await create_workspace_guard(data_dir="./data", backend=MockAdapter())
    await guard.add_workspace("alex", "alex@example.com")
    await guard.set_cap("alex", 1000)

    try:
        await guard.chat("alex@example.com", "hello")
    except QuotaExceededError:
        pass  # alex is over their monthly cap

    report = await guard.usage_report()

asyncio.run(main())

The library API is async (asyncio), matching the async architecture of the original TypeScript source rather than flattening it to synchronous calls.

How it works

target: an inbound chat request with an identity header value
   -> resolve_workspace() -- fail closed on any miss, never a default workspace
   -> check_quota() -- QuotaExceededError if the workspace is at its cap
   -> circuit breaker -- calls the BackendAdapter, opens after 3 consecutive
      failures, self-heals via a half-open probe after a cooldown
   -> record usage -- per-workspace, per-month counters with automatic
      period rollover
  • workspaceguard/isolation_guard.py -- the single choke point (chat()) every request flows through: resolve workspace -> check quota -> call backend -> record usage.
  • workspaceguard/usage.py -- the usage-metering engine this project adds: per-workspace, per-month counters with automatic period rollover, and QuotaExceededError enforcement.
  • workspaceguard/vault.py, workspaceguard/namespace.py, workspaceguard/circuit_breaker.py -- the original isolation-engine code, kept as the identity/workspace-boundary substrate the metering layer reads from, not shipped as a competing isolation product.
  • workspaceguard/adapters/ -- BackendAdapter abstract base class; a real Odysseus HTTP adapter is the next step (currently MockAdapter only, same as the TypeScript original).

Backend-specific behavior never enters the core modules directly -- everything goes through BackendAdapter.

Trust boundary

WorkspaceGuard trusts an upstream identity header (default: Cf-Access-Authenticated-User-Email) to resolve the workspace. It must never be directly reachable from the network -- only from behind whatever trusted proxy sets that header (Cloudflare Access, Tailscale, etc.). This is documented, not code-enforced.

What's real vs. not yet built

  • Real, tested (50/50 pytest tests passing): usage metering, quota enforcement, the original isolation engine (vault, namespace separation, circuit breaker), CLI with --json mode.
  • Not yet built: a real Odysseus HTTP adapter (only MockAdapter exists so far, same as the TypeScript original), a hosted managed billing dashboard (deliberately out of scope for this MIT project).

Security

The vault uses AES-256-GCM (via the cryptography package) with one derived key per workspace per generation -- rotating a workspace's key increments its generation, so old ciphertext genuinely can no longer be decrypted, not a no-op. See SECURITY.md for the disclosure process and what's in/out of scope.

Contributing

See CONTRIBUTING.md.

cd python
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest

License

MIT, see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

workspaceguard_cli-0.1.5.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

workspaceguard_cli-0.1.5-py3-none-any.whl (26.3 kB view details)

Uploaded Python 3

File details

Details for the file workspaceguard_cli-0.1.5.tar.gz.

File metadata

  • Download URL: workspaceguard_cli-0.1.5.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for workspaceguard_cli-0.1.5.tar.gz
Algorithm Hash digest
SHA256 b77c373e1d946c3b905cd34b1b51f46998fc3e31b508b08cc3842c4e17356b84
MD5 4b45fad151d38bbf245cdd3b0e36a9bf
BLAKE2b-256 4b13ab8f30c84b1984b16e9b9c9642736ba0a100de29f0b4329ef641ba9f093c

See more details on using hashes here.

Provenance

The following attestation bundles were made for workspaceguard_cli-0.1.5.tar.gz:

Publisher: publish-pypi.yml on RudrenduPaul/workspaceguard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file workspaceguard_cli-0.1.5-py3-none-any.whl.

File metadata

File hashes

Hashes for workspaceguard_cli-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 2860033cdf988b1aab6b53a144a950e0979e3fc3aa1bc16f515940b43d329214
MD5 9282fc771657d33cc7e95af17dd5786e
BLAKE2b-256 227ab575a6cb03cf23254c1198dd093d45eed0c375f719dd62e58c7095cd30ca

See more details on using hashes here.

Provenance

The following attestation bundles were made for workspaceguard_cli-0.1.5-py3-none-any.whl:

Publisher: publish-pypi.yml on RudrenduPaul/workspaceguard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page