git-trace

romele / git-trace public

62 commits · 1 branch · 1 tag · 1 contributor (top: Romele) · first commit 3d ago · last push 16m ago

doc/portal.md

# External management portal — billing, provisioning, SSO

git-trace deliberately ships **no** payment logic, no OAuth client,
no third-party SDK.  The forge binary is the trust boundary: it
speaks HTTP/Smart-HTTP/SSH and owns one SQLite file.  Everything
bureaucratic — signup funnels, Stripe, invoices, org management —
lives in a separate process that drives the forge from the outside.

This document is the contract between the two halves.

## The boundary

```
   ┌────────────────┐   subprocess   ┌──────────────────┐
   │ portal (any    │ ─────────────► │ git-trace CLI     │
   │ language:      │   or sqlite    │ (same host,       │
   │ sh/python/node │                │  same volume)     │
   │ Stripe, OAuth, │ ◄───────────── │                   │
   │ invoicing)     │   exit codes   └──────────────────┘
   └────────────────┘
```

The portal **never** links against the forge and never shares its
memory.  It has exactly two sanctioned interfaces:

1. **The CLI** (preferred).  Every account mutation the portal needs
   is already a subcommand:

   | Portal action            | Command                                              |
   |--------------------------|------------------------------------------------------|
   | create account           | `git-trace useradd --email=u@x alice` (pw on stdin)  |
   | reset password           | `git-trace passwd alice` (pw on stdin)               |
   | issue API token          | `git-trace mktoken alice portal`                     |
   | change plan/tier         | `git-trace tier alice pro +365` (`+days`, `YYYY-MM-DD`, or `never`) |
   | downgrade                | `git-trace tier alice free`                          |
   | change quota             | `git-trace quota alice 2048` (MiB; `0` = unlimited)  |
   | repo for a user          | `git-trace mkrepo [--private] alice name "desc"`     |
   | SSH keys                 | `git-trace sshkey list alice`, `sshkey rm <id>`      |
   | inspect state            | `git-trace tier alice`, `git-trace quota alice` — or read-only SQL on `gt.db` |

   Invoke the CLI as a dedicated OS user (same one the daemon runs
   as, e.g. `git`), capture the exit code — `0` = done.  This is the
   *only* write path recommended for production: it applies the same
   validation and audit trail as interactive use.

2. **Direct SQLite** (read-only, or carefully).  `gt.db` is a plain
   SQLite file; a portal may open it read-only for listings
   (`SELECT name, tier, tier_expires_at FROM users`) — SQLite WAL
   means readers never block the daemon.  Writing directly is
   possible but forfeits validation/audit; if you do it, keep writes
   idempotent and understand you are bypassing `db_audit`.

## Billing flow (reference)

```
Stripe webhook ─► portal verifies signature ─► payment ok
   └─► git-trace tier alice pro +365              (exit 0)
   └─► git-trace quota alice 2048                 (exit 0, MiB)
   └─► portal sends "welcome" mail with
       https://forge.example/register links (or pre-made account)
```

Subscription expiry needs no cron inside the forge: `tier_expires_at`
is already enforced by `auth.c` — when the clock passes the column,
the user reads as `free`.  A nightly `git-trace tier <u> free` sweep
from cron is optional housekeeping, not a requirement.

## Shared authentication (optional)

If the portal and the forge must appear as one site, put a reverse
proxy in front:

```
   browser ─► Caddy/Nginx ── /        ─► git-trace serve :8081
                        └── /portal  ─► portal :9000
```

- The forge must keep its own session cookie (`gt_session`) as the
  authority for `/` — do **not** inject `X-User` headers into the
  forge; it doesn't read them.  Federation of identity happens at
  account-provisioning time (the portal creates the account), not
  per-request.
- `GIT_TRACE_BASE_URL` tells the forge its public origin so
  same-origin POST checks keep working behind the proxy.
- TLS terminates at the proxy; the forge keeps listening on a
  loopback-only port.

## Rules for portal authors

- Never expose `gt.db` on a writable network share.
- The CLI is the API: parse nothing but exit codes and stdout.
- Password changes still go through `/account` in the forge UI, or
  `useradd`-equivalent flows owned by the portal — pick one to avoid
  confusion.
- The `admin` flag (`git-trace useradd --admin`) is the "root of the
  instance": grant it only through operator tooling, never from a
  webhook.
- Audit: every CLI mutation lands in `audit_log` — reconcile it
  from the portal side by diffing `users.tier` against your billing
  records nightly.