Metadata-Version: 2.4
Name: mact-cli
Version: 1.3.8
Summary: Mirrored Active Collaborative Tunnel — CLI to share live localhost previews with your team
License-Expression: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: System :: Networking
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.32.0
Provides-Extra: server
Requires-Dist: fastapi>=0.115.0; extra == "server"
Requires-Dist: uvicorn>=0.30.0; extra == "server"
Requires-Dist: httpx>=0.27.0; extra == "server"
Requires-Dist: sqlalchemy>=2.0.0; extra == "server"
Requires-Dist: alembic>=1.13.0; extra == "server"
Requires-Dist: psycopg[binary]>=3.1.0; extra == "server"
Requires-Dist: websockets<14,>=10.0; extra == "server"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: fastapi>=0.115.0; extra == "dev"
Requires-Dist: uvicorn>=0.30.0; extra == "dev"
Requires-Dist: httpx>=0.27.0; extra == "dev"
Requires-Dist: sqlalchemy>=2.0.0; extra == "dev"
Requires-Dist: alembic>=1.13.0; extra == "dev"
Requires-Dist: psycopg[binary]>=3.1.0; extra == "dev"
Requires-Dist: websockets<14,>=10.0; extra == "dev"

# MACT

**Mirrored Active Collaborative Tunnel** — route a stable public URL to the
localhost of the developer who last committed, with automatic fallback and an
offline page when nobody is connected.

Progress and planning live in **`project-tracking/`** (`PROGRESS.md`,
`ROADMAP.md`).

## Install

```bash
pip install mact-cli      # from PyPI (once published) — CLI only (requests + frpc download on first tunnel use)
# Self-host the control-plane API from the same repo / sdist:
pip install "mact-cli[server]"
# Editable for development (CLI + server deps + tests):
pip install -e ".[dev]"
```

If `frpc` is not on your `PATH`, the first `mact create` or `mact join` that starts a tunnel downloads a matching **frp** release into `~/.cache/mact/frp/<version>/` (override the tag with `MACT_FRP_VERSION`, e.g. `v0.61.0`).

## Quick start (developer)

Run all CLI commands from a **git checkout** of your project (`git init` if needed).

```bash
# 1. Set your identity
mact init --developer alice

# 2. (Optional) Configure tunnel — needed to expose your local port publicly
# Optional: defaults match public m-act.live frps (see mact.tunnel.constants)
mact configure-frp
# Self-hosted frps: pass --server-addr, --server-port, --auth-token explicitly

# 3. Create a room — from the project repo: installs the post-commit hook and ensures
#    frpc (if you will use a tunnel), then registers with the API, then starts tunnel + heartbeat
mact create --room demo --port 3000
#   → prints join_secret, admin_token, member_token
#   Use --no-live to skip tunnel/heartbeat; --no-hook to skip the hook; --no-tunnel for heartbeat-only.

# 4. Share the join secret with collaborators — they run (same auto-setup):
mact join --room demo --port 3001 --secret <join_secret>

# 5. Check room status anytime
mact status --room demo

# Optional: re-install only the post-commit hook (already done by create/join)
mact hook install --room demo
```

## Quick start (server)

Operators run the FastAPI app with **uvicorn** (not published as a separate console script on PyPI):

```bash
pip install "mact-cli[server]"
uvicorn mact.server.main:app --host 0.0.0.0 --port 8000
```

Default data store is **SQLite** at `./mact.db`. For production, use
**PostgreSQL** and run migrations (see Database migrations).

The marketing homepage at `/` (full product name, PyPI, and **GitHub: [int33k/M-ACT](https://github.com/int33k/M-ACT)**) is served by this FastAPI app. If **https://m-act.live/** still looks old, the droplet is running an older build: deploy the latest code and restart the API (Nginx already proxies `/` to uvicorn; there is no separate static `index.html` in the repo).

```bash
# On the server (adjust paths / venv / service name to match your setup)
cd /opt/mact   # or your clone of https://github.com/int33k/M-ACT
git pull origin main
./.venv/bin/pip install -e ".[server]"   # or: pip install -e .
sudo systemctl restart mact-server       # or: sudo systemctl restart uvicorn / your unit name
curl -sI https://m-act.live/ | grep -i cache-control   # should show no-store after deploy
```

**Edge routing (nginx):** Browsers use HTTPS. The checked-in `deployment/nginx/mact.live.conf` must be deployed with **TLS on both** `*.m-act.live` (mirror) and `*.tunnel.m-act.live` (frpc). If only HTTP is configured for wildcards, tunnel URLs hit the wrong vhost and break. After any nginx change: `python3 deployment/scripts/verify_edge_routing.py /etc/nginx/sites-enabled/mact.live.conf` then `sudo nginx -t && sudo systemctl reload nginx`. Full checklist: **`deployment/README.md`**.

## CLI commands

| Command | Description |
|---------|-------------|
| `mact --version` | Print CLI version |
| `mact ... --secure` | Optional: enable HTTPS certificate verification to the API |
| `mact init --developer <id>` | Set developer identity |
| `mact configure-frp ...` | Configure FRP tunnel settings |
| `mact create --room <id> --port <port> [...]` | Create room; ensures `frpc`, installs post-commit hook, then tunnel + heartbeat unless `--no-live` / `--no-hook` / `--no-tunnel` |
| `mact join --room <id> --port <port> --secret <s> [...]` | Same auto-setup as create |
| `mact leave --room <id>` | Disconnect from a room |
| `mact forget --room <id>` | Remove local room metadata from `~/.mact_rooms.json` (optional `--disconnect`) |
| `mact status --room <id>` | Fetch room status |
| `mact rooms` | List locally saved rooms |
| `mact hook install --room <id>` | Install git post-commit hook |
| `mact commit-report --room <id>` | Report HEAD commit (used by hook) |

## Environment (server)

| Variable | Default | Meaning |
|----------|---------|---------|
| `DATABASE_URL` | `sqlite:///./mact.db` | SQLAlchemy URL (`postgresql+psycopg://...` for Postgres) |
| `MACT_AUTO_CREATE_TABLES` | `true` | Create tables on startup (dev only) |
| `MACT_PRESENCE_TTL_SECONDS` | `60` | Member liveness TTL |
| `MACT_PUBLIC_BASE_DOMAIN` | `m-act.live` | Wildcard subdomain routing (`{room}.m-act.live`). Empty disables. |
| `MACT_ALLOW_LOOPBACK_UPSTREAM` | unset/false | If true, heartbeats may use `127.0.0.1` (local dev only; production should stay false so mirrors use tunnel URLs). |
| `MACT_DEV_DASHBOARD` | unset/false | Enable dev-only path dashboard routes (`/rooms/{id}/dashboard`) |

## Environment (CLI)

| Variable | Default | Meaning |
|----------|---------|---------|
| `MACT_BACKEND_URL` | `https://m-act.live` | Control-plane API base URL used by CLI commands |
| `MACT_FRP_AUTH_TOKEN` | unset | Overrides the built-in default token (same as public `frps.toml` on m-act.live); also used when not saved in `~/.mact_config.json` |
| `MACT_FRP_VERSION` | `v0.61.0` | FRP release tag auto-downloaded when `frpc` is missing |
| `MACT_CA_BUNDLE` | unset | Path to a PEM file for HTTPS to the API (enables verify with that CA) |
| `MACT_SSL_VERIFY` | `false` (default) | Set to `true` to verify certificates (same idea as `--secure`) |
| `REQUESTS_CA_BUNDLE` | unset | Also honored by `requests` (corporate CA bundle) |

By default the CLI does **not** verify TLS to the API (avoids issues behind proxies/antivirus). Use **`--secure`** or **`MACT_SSL_VERIFY=true`** when you want full verification, or **`MACT_CA_BUNDLE`** / **`REQUESTS_CA_BUNDLE`** to trust a custom root CA.

**FRP tunnel:** The CLI ships **defaults** for the public **m-act.live** frps (`mact configure-frp` with no args). Self-hosted operators must pass **`--auth-token`** matching **`auth.token`** in **frps**. If the token does not match, frpc never registers the proxy and the tunnel URL shows frp’s “The page you requested was not found.” The generated **frpc** config sets **`transport.tls.enable = false`** for the control connection (frp v0.50+ defaults to TLS; most frps use plain TCP on **:7000**). If your frps uses TLS on the control port, set **`MACT_FRPC_TRANSPORT_TLS=1`**. **Developer/room IDs** with underscores are normalized to hyphens in the tunnel hostname so **`customDomains`** matches the browser **Host** header. **Local proof:** `./local_e2e.sh --quick --with-tunnels` starts **frps**, runs the same tunnel + heartbeat logic as **`mact create`/`join`**, and curls the vhost.

## API endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/health` | — | Liveness check |
| `GET` | `/ready` | — | Readiness (DB reachable) |
| `POST` | `/rooms` | — | Create room → tokens |
| `POST` | `/rooms/{id}/join` | join_secret in body | Join room → member_token |
| `POST` | `/rooms/{id}/commits` | Bearer member_token | Report commit |
| `PATCH` | `/rooms/{id}/commits/{hash}/comment` | Bearer admin_token | Set/clear admin comment |
| `GET` | `/rooms/{id}/status` | — | Room status JSON |
| `POST` | `/rooms/{id}/presence/heartbeat` | Bearer member_token | Heartbeat (+ optional upstream update) |
| `POST` | `/rooms/{id}/presence/disconnect` | Bearer member_token | Disconnect |
| `GET` | `/r/{id}[/path]` | — | Mirror HTTP proxy to active upstream |
| `WS` | `/r/{id}[/path]` | — | Mirror WebSocket to active upstream (same path/query as HTTP) |
| `GET` | `/dashboard` | — | Room dashboard (subdomain only) |

## Public URLs (production)

With `MACT_PUBLIC_BASE_DOMAIN=m-act.live` and DNS `*.m-act.live` → server:

- **Mirror**: `https://{room}.m-act.live/`
- **Dashboard**: `https://{room}.m-act.live/dashboard`
- **Tunnel vhosts**: `https://{dev}-{room}.tunnel.m-act.live` (labels normalized, e.g. underscores → hyphens; via frps + nginx TLS; override with `mact configure-frp --public-scheme http` for local dev)

Path-based routing (`/r/{room}`) remains for local dev or when subdomain routing
is disabled.

Expose the API only behind trusted network controls (private network, VPN, or auth
gateway). By design, room creation/status endpoints are open unless you add an
external access-control layer.

## Database migrations

```bash
cd "/path/to/M ACT"
alembic upgrade head
```

Set `MACT_AUTO_CREATE_TABLES=false` when using Alembic in production.

## Tests

```bash
pip install -e ".[dev]"
pytest -v
```

## Architecture

See `ARCHITECTURE.md` for the full design, phased delivery plan, security notes,
and distributed systems considerations.
