Skip to main content
Glama
aranlucas

set-and-signal

by aranlucas
README.md
<p align="center">
  <img src="web/public/icon-512.png" alt="Set & Signal icon" width="112" />
</p>

# Set & Signal ๐Ÿ‹๏ธ ยท Make the next set obvious

[![TypeScript checks](https://github.com/aranlucas/set-and-signal/actions/workflows/typecheck.yml/badge.svg)](https://github.com/aranlucas/set-and-signal/actions/workflows/typecheck.yml)
[![Go checks](https://github.com/aranlucas/set-and-signal/actions/workflows/go.yml/badge.svg)](https://github.com/aranlucas/set-and-signal/actions/workflows/go.yml)
[![License](https://img.shields.io/github/license/aranlucas/set-and-signal)](LICENSE)

Set & Signal is a self-hostable workout planner and training log for planning
sessions, recording sets, reviewing history, and asking an AI assistant about
training data. The product ships as a React/Vite web app embedded in a Go
server, with passkeys/OIDC sign-in, Convex-backed training records, SQLite for
identity and operational state, and an MCP endpoint for assistant clients.

> **A session in the real world:** plan a push day, log the working sets as you
> go, then ask the assistant what to train next. Your history, prescription,
> and account boundary stay in the same self-hosted system.

The project is designed for a single deployment you control. It includes an
exercise catalogue, progression and prescription helpers, workout history,
bodyweight tracking, reminders, push notifications, localization, and a
read/write MCP surface protected by the same account boundary as the web app.

## Run locally

Requires Go 1.27+, Node.js 24+, pnpm 12+, PostgreSQL-compatible Convex
deployment, and an OAuth or passkey configuration. The API refuses to start
without `CONVEX_URL`; the full training setup and storage rules are in
[docs/convex.md](docs/convex.md).

Start the API and web app together, then open `https://set-and-signal.localhost`:

```bash
pnpm install --frozen-lockfile
pnpm dev
```

`pnpm dev` runs `dev:api` and `dev:web` in parallel. The API reads `CONVEX_URL` from
`web/.env.local` and stores data in `./data`.

Both run through [Portless](https://github.com/vercel-labs/portless) (a dev
dependency): the API at `https://api.set-and-signal.localhost`, the web app at
`https://set-and-signal.localhost`. The first run may ask for `sudo` to bind port 443
and trust a local certificate. Vite proxies `/api`, `/oauth`, `/.well-known`, and
`/mcp` to the API, so OAuth callbacks, session cookies, and the local MCP endpoint
all use the web origin. Passkeys registered for `localhost` do not carry over to the
new hostname.

For a fresh development deployment, run `pnpm exec convex dev` from `web` and
follow the setup guide to configure `AUTH_ISSUER` and `AUTH_JWKS`. Keep the
generated `web/.env.local`, signing keys, and any provider credentials out of
Git.

## Verify

```bash
go test -race ./...
go vet ./...
(cd .railway && go test ./... && go vet ./...)
```

The service is licensed under the [GNU AGPL v3 or later](LICENSE). See [`SECURITY.md`](SECURITY.md) for vulnerability reports.

## Architecture

```mermaid
flowchart LR
  Browser[React/Vite web app] --> API[Go HTTP API]
  Browser --> Convex[Convex training functions]
  API --> SQLite[(SQLite identity + operations)]
  API --> Convex
  API --> MCP[MCP Streamable HTTP]
  API --> Auth[Passkeys / OIDC]
  API --> Push[Web Push + reminders]
```

The Go composition root is `cmd/opengym-api`. HTTP and MCP protocol concerns
live in `internal/httpapi`; training state, analytics, scheduling, and
progression live in `internal/training`; SQLite access and migrations live in
`internal/store`. The frontend follows the `app โ†’ features โ†’ domain โ†’ shared`
direction described in [docs/architecture.md](docs/architecture.md). Edit
exercise content in `web/catalog`; generated instruction files are rebuilt by
the web scripts.

## Configuration essentials

`.env.example` documents the complete runtime surface. The settings most
people need are:

| Variable | Why it matters |
| --- | --- |
| `CONVEX_URL` | Required hosted training deployment. |
| `DATA_DIR` / `DB_PATH` | Persistent SQLite, session, push, and signing-key storage. |
| `PUBLIC_URL` / `ORIGIN` | Browser origin and OAuth/MCP issuer; use a reachable HTTPS URL in production. |
| `RP_ID` / `RP_NAME` | WebAuthn relying-party identity. |
| `GOOGLE_*`, `GITHUB_*`, `APPLE_*` | Optional OIDC providers for sign-in and MCP OAuth. |
| `OPENROUTER_API_KEY` / `OPENROUTER_MODEL` | Optional AI workout planning. |

## Further documentation

- [Architecture](docs/architecture.md) โ€” dependency direction and build layout.
- [Convex setup and storage](docs/convex.md) โ€” hosted training ownership,
  offline queues, keys, and current format requirements.
- [Data model](docs/data-model.md) โ€” storage ownership and current contracts.
- [Product](docs/product.md) and [design](docs/design.md) โ€” user flows and UI
  constraints.
- [Railway deployment](.railway/README.md) โ€” infrastructure configuration.

## Status and privacy

The repository is an active, self-hosted application with hosted
Convex training storage. Training records are account-scoped, but a deployment's
operator controls the API, Convex project, and persistent volume. Review
`SECURITY.md` and configure access controls before exposing an instance to
other users.