Ctrl K
ComunidadGuías…

docs/getting-started.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
```
╔════════════════════════════════════════════════════════════════════════════╗
║                                                                            ║
║   GETTING STARTED                                                          ║
║   From a clean machine to a running Cloud Codex in five minutes.           ║
║                                                                            ║
╚════════════════════════════════════════════════════════════════════════════╝
```

# Getting Started

This guide covers everything you need to run Cloud Codex locally — from
prerequisites through a working application with optional sample data.

```
   ┌─────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
   │  clone  │──►│ cp .env  │──►│ ./start  │──►│ first    │──►│ invite   │
   │  repo   │   │ .example │   │   .sh    │   │ admin    │   │ teammates│
   └─────────┘   └──────────┘   └──────────┘   │ login    │   └──────────┘
                      │              │         └──────────┘
                      ▼              ▼
                 fill DB,       installs deps
                 admin values   boots MySQL
                 (SMTP is       starts Vite + API
                 optional)
```

---

## Prerequisites

- **Linux** or **Windows Subsystem for Linux (WSL)**
- **Docker** with Compose v2
- **Node.js** 20 or later
- **npm**

> On Debian/Ubuntu, the included startup script can detect and install missing system packages automatically.

---

## 1. Clone the repository

```bash
git clone <repository-url>
cd c2
```

---

## 2. Configure environment variables

```bash
cp .env.example .env
```

Open `.env` and fill in the required values. At minimum you need database credentials and admin account credentials. **SMTP is optional**: leave it blank and the server still starts. With mail disabled: invitations are still created and their signup link is shown copyable in the admin UI instead of emailed, password reset reports itself unavailable rather than silently failing, and email-based two-factor authentication is refused (authenticator-app TOTP still works, showing its QR code and secret in the app instead of emailing them). Squad invitations still work either way and still raise the in-app notification. **If you turn SMTP off on an instance that already had it**, note that any account already using email 2FA can no longer log in, and no account can turn 2FA off, because both flows confirm with an emailed code. An administrator clears that from the Admin console, or with `POST /api/admin/users/:id/2fa/reset`, without needing mail.

```dotenv
# ─── Database ────────────────────────────────────────────────
DB_HOST=localhost
DB_USER=admin
DB_PASS=changeme
DB_NAME=c2
MYSQL_ROOT_PASSWORD=changeme

# ─── App ─────────────────────────────────────────────────────
APP_URL=http://localhost:3000
CORS_ORIGIN=

# ─── Admin ───────────────────────────────────────────────────
ADMIN_USERNAME=admin
ADMIN_EMAIL=
ADMIN_PASSWORD=

# ─── SMTP (optional — leave blank to run without email) ─────
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM=

# ─── OAuth (optional — enables SSO and GitHub integration) ───
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_OAUTH_DOMAIN=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

# ─── Runtime (optional) ──────────────────────────────────────
# NODE_ENV=
```

---

## 3. Start the application

### Quickstart (recommended)

```bash
./start.sh
```

This script will:
1. Verify and install system dependencies (Docker, Node.js, npm, mysql-client)
2. Start the Docker daemon if it is not already running
3. Launch the MySQL 8 container via Docker Compose
4. Wait for the database to accept connections
5. Install npm dependencies
6. Start the development server on **http://localhost:3000**

### Manual setup

If you prefer to start services individually:

```bash
# Start the MySQL container
docker compose up -d

# Install dependencies
cd cloudcodex
npm install

# Start the dev server
npm run dev
```

The application will be available at **http://localhost:3000**.

MySQL is published on **127.0.0.1:3306** only, which the dev server, `make`
and a `mysql` client on the same machine reach (`DB_HOST=localhost` works).
Nothing else on your network can reach it, since it runs with a development
password. Set `DB_BIND=0.0.0.0` in `.env` only if you deliberately need it
from another machine. This loopback binding has not been tested under WSL; if
a client there cannot reach `127.0.0.1:3306`, `DB_BIND=0.0.0.0` restores the
previous mapping.

On first boot, once the admin logs in, they land inside a seeded "Getting
Started" archive with a "Welcome to Cloud Codex" document, not an empty app.
This only happens on a database that holds no workspaces, archives or logs at
all; it never touches an install with any content in it.

---

## 4. Load sample data (optional)

A seed script populates the database with a workspace, three squads, eight archives, and ~60 logs of realistic content. This is useful for testing search, pagination, browsing, and collaboration features before creating real content.

```bash
mysql -u $DB_USER -p -h 127.0.0.1 c2 < seed.sql
```

All seed accounts use the password **`password`**.

| Account | Email | Role / Notes |
| --- | --- | --- |
| `alice` | alice@acme.com | Workspace owner. Engineering squad owner. Full permissions. |
| `bob` | bob@acme.com | Engineering and Operations member. Can create archives and logs. |
| `carol` | carol@acme.com | Design squad owner. Can create archives and logs. |
| `dave` | dave@acme.com | Operations squad owner. Can create archives and logs. |
| `eve` | eve@acme.com | Engineering member and Data Pipeline lead. Can create logs only. |

**Squads and archives:**

| Squad | Owner | Archives |
| --- | --- | --- |
| Engineering | alice | Platform API, Cloud Infrastructure, Mobile App, Data Pipeline |
| Design | carol | Brand Guidelines, Website Redesign |
| Operations | dave | Incident Runbooks, Onboarding |

---

## Environment Variables

| Variable | Description | Default |
| --- | --- | --- |
| `DB_HOST` | MySQL server hostname | `localhost` |
| `DB_USER` | MySQL username | — (required) |
| `DB_PASS` | MySQL password | — (required) |
| `DB_NAME` | MySQL database name | `c2` |
| `DB_POOL_SIZE` | MySQL connections the app keeps open, 1 to 100; anything else stops the boot | `10` |
| `MYSQL_ROOT_PASSWORD` | Root password for the Docker MySQL instance | — (required) |
| `APP_URL` | Base URL used to build invitation/reset links, in emails and in the admin UI's copyable link. **Required in production**: the server will not start without an `http(s)` URL | `http://localhost:3000` in development |
| `PORT` | The port the app listens on; a value that is not a valid port stops the boot | `3000` |
| `TRUST_PROXY` | Which proxies Express believes about the client address, named by address (an address or CIDR list in standard notation, subnet names such as `loopback`, or `false`); the rate limiters count by that address. A hop count, `true`, or a range wider than an IPv4 /8 or an IPv6 /16 (outside `fc00::/7` and `fe80::/10`) stops the boot | `127.0.0.1/32, ::1/128, 172.29.0.1/32` (a proxy on this host, in front of either compose file) |
| `TRUST_PROXY_ALLOW_HOP_COUNT` | `true` accepts a hop count, `true` or an over-wide range in `TRUST_PROXY` anyway, letting any client that can reach the port choose its own address | unset (off) |
| `APP_BIND` | Docker Compose only: the host address the app port is published on. Set `0.0.0.0` only to reach the app from other machines on purpose; an IPv4 address only, never `::` | `127.0.0.1` |
| `DB_BIND` | `docker-compose.yaml` (dev) and `docker-compose-prod.yml`: the host address MySQL's 3306 is published on | `127.0.0.1` |
| `CORS_ORIGIN` | One more origin allowed to call the API, for a separate front end. The app's own origin and `APP_URL`'s are always allowed | unset |
| `C2_INSTANCE_LOCK` | The one-process-per-database lock: a second process on the same schema refuses to start. Only `0` turns it off, as an escape, not a way to run replicas | `1` |
| `DOC_IMAGES_PUBLIC` | Exactly `1` serves document images to anyone with their address, as releases before the per-reader check did. Only for an upgrade that has to start before `npm run backfill:doc-images` has run; see [deployment.md](./deployment.md#the-document-images-backfill-once) | unset (off) |
| `SMTP_HOST` | SMTP server hostname | — (optional; leave blank to run without email) |
| `SMTP_PORT` | SMTP server port | `587` |
| `SMTP_USER` | SMTP username | — (optional; leave blank to run without email) |
| `SMTP_PASS` | SMTP password | — (optional; leave blank to run without email) |
| `SMTP_FROM` | Sender address for outbound email; blank uses `Cloud Codex <noreply@…>` with the host from `APP_URL` (set `SMTP_FROM` to a sender on a domain you control if your mail host requires one) | `Cloud Codex <noreply@<APP_URL host>>` |
| `ADMIN_USERNAME` | Username for the admin super-user. Boot creates it, or syncs an existing admin, and never promotes a non-admin (`.env.example` ships `admin`). Required while local sign-in is on; on an OIDC-only instance (`AUTH_PROVIDERS` without `local`) unset gives the admin a name derived from `ADMIN_EMAIL` | (required with local sign-in) |
| `ADMIN_EMAIL` | Email address for the auto-created admin super-user | — (required) |
| `ADMIN_PASSWORD` | Password for the admin super-user. Required while local sign-in is on; an OIDC-only instance never reads it | none (required with local sign-in) |
| `GOOGLE_CLIENT_ID` | Google OAuth client ID (enables Google SSO) | — |
| `GOOGLE_CLIENT_SECRET` | Google OAuth client secret | — |
| `GOOGLE_OAUTH_DOMAIN` | Restrict Google SSO to a specific email domain | — |
| `OIDC_ISSUER_URL` | The OIDC issuer to sign people in through (enables OIDC sign-in with `OIDC_CLIENT_ID` and `OIDC_CLIENT_SECRET`). `https`, or plain `http` on `localhost`, `127.0.0.1`, `::1` or a `*.localhost` name only | — |
| `OIDC_CLIENT_ID` | This instance's application client ID at the issuer. Set it with `OIDC_ISSUER_URL` | — |
| `OIDC_CLIENT_SECRET` | That application's client secret. Set it with `OIDC_ISSUER_URL` | — |
| `OIDC_PROVIDER_NAME` | The label on the sign-in button, "Sign in with <name>" | `SSO` |
| `OIDC_LINK_BY_VERIFIED_EMAIL` | `1` links an existing account by its verified email when that account has two-factor off; `0` never links by email | `1` (on) |
| `OIDC_SESSION_TTL_HOURS` | How long an OIDC sign-in lasts, in whole hours | `24` |

The redirect and logout URIs an issuer must register, the provider notes, and the OIDC-only setup are in [sign-in.md](./sign-in.md).
| `LEGACY_SESSION_COOKIE` | Whether a lone `sessionToken` cookie from before the https session cookie became `__Host-sessionToken` still signs its holder in. Only `0` turns it off (for an https instance sharing its domain with hosts you do not control) | `1` (on) |
| `AUTH_PROVIDERS` | Sign-in methods to offer, a comma list of `local`, `google` and `oidc`. Leave unset: the server derives it (local, plus Google when configured) and refuses to start on a value that disagrees with the Google variables. A list without `local` is an OIDC-only instance: it must be `oidc` alone, the Google variables must be unset, and local sign-in, sign-up, reset, 2FA and local email change are refused on the server | unset |
| `GITHUB_CLIENT_ID` | GitHub OAuth application client ID | — |
| `GITHUB_CLIENT_SECRET` | GitHub OAuth application client secret | — |
| `NODE_ENV` | `production` serves the built frontend and puts the security headers on every response; the Docker image and `npm run start` set it. In every mode the API accepts the app's own origin, `APP_URL`'s and `CORS_ORIGIN`; outside production it also accepts any `localhost` or `127.0.0.1` origin, for the Vite dev server. `test` switches the rate limiters off | unset (development) |
| `WEBHOOK_ALLOW_PRIVATE_TARGETS` | `1` lets any webhook reach loopback and private addresses; link-local and metadata addresses are always refused | unset (optional) |

---

## Removed variables

These variables were read by an earlier release and are no longer read for any behaviour. Setting one
prints a line at boot naming it, then the server starts as it would without it. Unset them.

| Variable | What to do |
|---|---|
| `SUITE_COMMAND_WORKSPACE_URL` | the suite integration was removed in 0.15.0. Accounts linked to an identity provider now accept a local password and local two-factor; set AUTH_PROVIDERS=oidc to keep sign-in provider-only. Unset it |
| `SUITE_NAME` | the suite integration was removed in 0.15.0; unset it |
| `SUITE_PREFERENCE_DOMAIN` | the theme and accent preference is per host now; unset it |
| `OIDC_ACCOUNT_URL` | accounts are editable in Codex now; unset it |
| `SERVICE_TOKEN` | the static machine credential was removed in 0.15.0; a script signs in as a user and sends that session. Unset it |
| `SERVICE_TOKEN_USER` | the machine credential's account was removed in 0.15.0; the account stays an ordinary user. Unset it |
| `MACHINE_OIDC_AUDIENCE` | machine JWTs were removed in 0.15.0; the OIDC sign-in for people is unaffected. Unset it |
| `MACHINE_OIDC_SUBJECTS` | machine JWTs were removed in 0.15.0; the OIDC sign-in for people is unaffected. Unset it |
| `WEBHOOK_URL` | the environment-declared webhook subscription was removed in 0.15.0; create it in Admin, Webhooks, then unset it |
| `WEBHOOK_SECRET` | the environment-declared webhook subscription was removed in 0.15.0; the console shows its new secret once; unset this |
| `WEBHOOK_WORKSPACE_ID` | the environment-declared webhook subscription was removed in 0.15.0; set the workspace filter in Admin, Webhooks; unset it |

## NPM Scripts

Run from the `cloudcodex/` directory:

| Command | Description |
| --- | --- |
| `npm run dev` | Start the development server with hot reload |
| `npm run build` | Build the frontend for production |
| `npm run preview` | Preview the production build locally |
| `npm run lint` | Run ESLint across the codebase |
| `npm test` | Run the full test suite |
| `npm run test:watch` | Run tests in watch mode |
| `npm run test:coverage` | Run tests with code coverage reporting |

---

## Makefile Targets

Run from the project root:

| Target | Description |
| --- | --- |
| `make seed` | Load seed data (wipes existing data first) |
| `make reset-db` | Re-run the init.sql schema then seed |
| `make db-shell` | Open a MySQL shell in the Docker container |