SETUP.md
Rama main · réplica de lectura del repositorio original.
# Local workstation setup
Bootstrap a fresh machine (laptop, ephemeral VM, codespace) to run, test, and deploy Managoat. Should take ~10 minutes on broadband.
## Prerequisites
| Tool | Why | Install |
| ---------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `mise` | Pins Elixir/Erlang to the exact versions in `.tool-versions`. | `brew install mise` (macOS) · `curl https://mise.run \| sh` (Linux/WSL) |
| `gh` | GitHub CLI for cloning and PRs. | `brew install gh && gh auth login` |
| `psql` | Client for the dev/test Postgres. | Comes with `brew install postgresql@16`. Match the server major (16): an older `pg_dump` refuses a newer server, which is the backup drill. |
| Docker *or* native Postgres 16 | One way to host the dev/test database. | `brew install --cask orbstack` (or Docker Desktop), or `brew install postgresql@16 && brew services start postgresql@16`. |
After installing `mise`, [activate it in your shell](https://mise.jdx.dev/getting-started.html#activate-mise):
```bash
# zsh
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
# bash
echo 'eval "$(mise activate bash)"' >> ~/.bashrc
```
Open a new shell so `mise` is on the PATH.
## 1. Clone
```bash
gh repo clone managoat/managoat
cd fountain
```
## 2. Install the toolchain
```bash
mise install
```
This reads `.tool-versions` and installs the pinned Erlang/OTP and Elixir versions (~5 min on first run, cached after). `.tool-versions` pins the *local* toolchain only; production runs the release image built from the repo-root `Dockerfile`, whose base image pins its own Erlang/Elixir (see [Production parity reference](#production-parity-reference)).
Verify:
```bash
elixir --version
# Erlang/OTP 28 ...
# Elixir 1.19.2 (compiled with Erlang/OTP 28)
```
## 3. Hex + Rebar (one-time, per-toolchain)
```bash
mix local.hex --force
mix local.rebar --force
```
## 4. Postgres
Pick **one** path:
### Option A — Docker (recommended for ephemeral machines)
```bash
docker compose up -d postgres
```
Brings up Postgres 16 on `localhost:5432` with role `postgres`/`postgres` (already running your own Postgres? set `POSTGRES_HOST_PORT` in `.env` to publish elsewhere). `mix setup` creates `fountain_dev` itself. (`docker compose up` with no service name also starts Managoat — see [self-hosting](docs/self-hosting.md).) Stop with `docker compose down`; data persists in the `postgres_data` volume.
### Option B — Native Postgres
If you already run Postgres locally (e.g. `brew services start postgresql@16`), make sure a `postgres` superuser role exists with password `postgres`:
```bash
psql -h localhost -U "$USER" -d postgres \
-c "CREATE ROLE postgres WITH LOGIN SUPERUSER PASSWORD 'postgres'"
```
(Skip if you already have it.)
Both paths satisfy the default `DATABASE_URL` baked into `config/dev.exs` and `config/test.exs`. To override, export `DATABASE_URL` before any `mix` command.
## 5. Dependencies and database
```bash
mix deps.get
mix setup # deps.get, acp.bundles (the pinned adapter bundles) + ecto.setup for the dev DB
```
For the test DB:
```bash
MIX_ENV=test mix ecto.create
MIX_ENV=test mix ecto.migrate
```
## 6. Environment variables
`config/runtime.exs` reads a `.env` at the repo root. Start from the local-dev subset:
```bash
cp .env.example .env
```
| Variable | Purpose |
|---|---|
| `DATABASE_URL` | The Postgres connection (the default matches both options in step 4). |
| `MASTER_SECRETS_KEY` | The platform master key for envelope encryption. Make one with `openssl rand 32 \| base64 \| tr '+/' '-_' \| tr -d '='`. |
| `SPRITES_TOKEN` | The Sprites sandbox platform; without a provider every conversation fails. |
| `GITHUB_OAUTH_CLIENT_ID` / `_SECRET` | GitHub sign-in, optional. |
| `SMTP_HOST` and `EMAIL_FROM` | Email over SMTP, optional. Unset, mail is off and accounts self-verify. |
The full list is [docs/configuration.md](docs/configuration.md).
## 7. Run
```bash
mix phx.server # http://localhost:4000
mix test # full suite
```
`mix precommit` runs the local gate; its stages, and what CI runs on top, are in [CONTRIBUTING.md](CONTRIBUTING.md#before-you-push). Run it through the pinned toolchain (activate mise, or `mise exec -- mix precommit`); it refuses a different Elixir.
## Production parity reference
The release image is built from the repo-root `Dockerfile`, and `deploy/` holds the portable Kubernetes baseline (decisions/0032). The Erlang/Elixir toolchain is pinned in three places, and a toolchain bump must update all of them:
| Where | What pins the toolchain |
| -------------------------- | -------------------------------------------------------------------- |
| `.tool-versions` | `erlang` / `elixir` lines (local dev via mise) |
| `Dockerfile` | the `FROM hexpm/elixir:<elixir>-erlang-<otp>-...` build-stage base image |
| `.github/workflows/ci.yml` | `elixir-version` / `otp-version` in the `erlef/setup-beam` step |
The lockstep is enforced by `apps/fountain/test/fountain/toolchain_lockstep_test.exs` — if the three pins disagree, the test suite goes red.
## Troubleshooting
- **`role "postgres" does not exist`** — see step 4. Either `docker compose up -d postgres` or create the native role.
- **Compile warnings about `OpentelemetryPhoenix` / `OpentelemetryEcto`** — these deps are `:prod`-only; `apply/3` is used to defer symbol resolution. If you see warnings, you're probably on stale `_build`; `rm -rf _build deps && mix deps.get && mix compile` to reset.
- **mise installs Erlang from source and it's slow** — that's normal on first run. Subsequent installs (and ephemeral machines that share a mise cache) reuse the build.
- **Tests fail with connection pool timeouts** — check that `config/test.exs` still sets `pool_size: 20`, and lower `--max-cases` on a machine with many cores.
- **Tests fail with `rate_limited` errors** — shouldn't happen: `config/test.exs` sets `rate_limit_test_isolation: true`, which keys `FountainWeb.Plugs.RateLimit` buckets by the calling test process instead of a shared IP. If you see this, check that the flag is still set in `config/test.exs` (it's a correctness guard — don't remove it) and that your test isn't issuing enough requests from one process to legitimately exceed a bucket's limit.