115 lines
7.2 KiB
Markdown
115 lines
7.2 KiB
Markdown
# Project
|
|
|
|
## Environment
|
|
|
|
You are working inside a VCH cloud development environment.
|
|
|
|
- **OS:** Debian-based LXC container
|
|
- **Editor:** code-server (VS Code in browser) on port 8080
|
|
- **CLI:** Claude Code is available as `claude` in terminal
|
|
- **Git:** Pre-configured. Gitea integration available through VCH.
|
|
- **Preview:** Start a dev server on any port — VCH detects listening ports automatically and provides preview URLs with SSL.
|
|
- **Publishing:** Assign a public subdomain to any port via the VCH Publish page (automatic SSL).
|
|
- **User:** `vchuser` with home at `/home/vchuser`
|
|
|
|
## Preview Access (IMPORTANT)
|
|
|
|
Apps are accessed through a **reverse proxy**, not directly. There are two modes:
|
|
|
|
1. **Subdomain proxy** (preferred): `https://lxc{VMID}-{PORT}.dev.example.com/` — your app is at the domain root, everything works normally.
|
|
2. **Path-based proxy** (fallback): `https://host/proxy/{INSTANCE}/p/{PORT}/` — your app is behind a path prefix.
|
|
|
|
### Rules for portable apps that work in both modes:
|
|
|
|
- **ALWAYS use relative paths** for assets, API calls, and links:
|
|
- `css/style.css` ✅ — NOT `/css/style.css` ❌
|
|
- `api/users` ✅ — NOT `/api/users` ❌
|
|
- `fetch('api/data')` ✅ — NOT `fetch('/api/data')` ❌
|
|
- **Static file serving:** Configure your server to serve from the request path, not the filesystem root.
|
|
- **`<base>` tag:** If you must use absolute paths, add `<base href="./">` in `<head>`.
|
|
|
|
### Framework-specific configuration:
|
|
|
|
- **Express:** `app.use(express.static('public'))` works — just ensure HTML references are relative.
|
|
- **Vite:** Set `base: './'` in `vite.config.ts`.
|
|
- **Next.js:** Set `basePath` in `next.config.js` if using path-based proxy, or leave default for subdomain proxy.
|
|
- **Create React App:** Set `"homepage": "."` in `package.json`.
|
|
|
|
## Development Conventions
|
|
|
|
- Write clear, descriptive commit messages (imperative mood: "Add feature", not "Added feature")
|
|
- Prefer small, focused commits over large ones
|
|
- Run linters and formatters before committing
|
|
- Write tests for critical business logic
|
|
- Keep dependencies minimal — use native browser/Node APIs where possible
|
|
|
|
## README Requirement (MANDATORY)
|
|
|
|
Every project MUST have a meaningful `README.md` in the project root. This is enforced by the VCH audit system — projects without a proper README cannot be published.
|
|
|
|
Your README must include at minimum:
|
|
- **Project description** — what the project does (not just the template placeholder)
|
|
- **Installation instructions** — how to install dependencies
|
|
- **Development instructions** — how to start the dev server
|
|
- **Tech stack** — what technologies are used
|
|
|
|
Update the README whenever you add features, change setup steps, or modify the tech stack. The audit will reject READMEs that are still just the default template.
|
|
|
|
## Project Description (MANDATORY)
|
|
|
|
Every project has a `.vch-description` file in the project root. Keep this file up to date with a clear, non-technical description of your project:
|
|
- What the project does
|
|
- What problem it solves or what it's used for
|
|
- Who it's for
|
|
|
|
Do NOT include technical details (tech stack, dependencies, setup instructions) — those belong in the README. The `.vch-description` content is shown in the VCH Showcase and is synced automatically when an audit runs.
|
|
|
|
Update `.vch-description` whenever the project's purpose or scope changes significantly.
|
|
|
|
## Web Best Practices
|
|
|
|
- Use semantic HTML elements (`nav`, `main`, `article`, `section`, etc.)
|
|
- Mobile-first responsive design
|
|
- Follow accessibility guidelines (ARIA labels, keyboard navigation, color contrast)
|
|
- Optimize images and assets for performance
|
|
- Use environment variables for configuration — never hardcode secrets
|
|
|
|
## Commands
|
|
|
|
Fill in project-specific commands below:
|
|
|
|
- **Install dependencies:** `npm install`
|
|
- **Start dev server:** `npm run dev`
|
|
- **Run tests:** `npm test`
|
|
- **Build for production:** `npm run build`
|
|
- **Lint:** `npm run lint`
|
|
|
|
## Deploy preparation
|
|
|
|
If this project should be deployed and reads environment variables from `process.env` (or equivalent), create `.env.example` at the repo root. List every variable the app reads, one per line: `KEY=example-value`. Example values are hints only — they are **not** used in production. If the project needs no env vars, no file is required.
|
|
|
|
Vibecoders' tip: when in doubt, run `grep -RhoE "process\.env\.[A-Z_]+" src/ | sort -u` and put every result into `.env.example`.
|
|
|
|
**Listen on `PORT` (IMPORTANT).** VCH assigns every deployed app its own host port automatically — you never pick or coordinate ports, and two projects on the same machine never clash. Your app MUST bind the port from the `PORT` environment variable, not a hardcoded one:
|
|
|
|
- Node/Express: `app.listen(process.env.PORT || 3000)`
|
|
- Next.js: `next start` honours `PORT` automatically
|
|
- Vite preview / other servers: pass `--port "$PORT"` (or read `process.env.PORT`)
|
|
|
|
A hardcoded port works locally but fails the production health-check (VCH checks the assigned port, which usually isn't 3000). For Docker, set `port:` below to the port your app listens on *inside* the container — VCH maps the auto-assigned host port to it for you.
|
|
|
|
**Containerized projects (Docker).** If your project has a `Dockerfile` (or a `compose.yaml`/`docker-compose.yml`), add a `.vch/deploy.yaml` at the repo root so VCH deploys it correctly:
|
|
|
|
```yaml
|
|
runtime: docker
|
|
port: 3000 # the port your app LISTENS ON inside the container
|
|
health: / # a path that returns 2xx/3xx when the app is up
|
|
```
|
|
|
|
- VCH builds your image, runs the container with `--restart=always`, injects production env vars via `--env-file` at runtime, and health-checks `port`.
|
|
- **Build-time variables:** only variables prefixed `NEXT_PUBLIC_`, `VITE_`, or `PUBLIC_` are passed to the build as `--build-arg` (they are client-visible by convention). Secrets are runtime-only and are never baked into the image — declare matching `ARG` lines in your Dockerfile for the public ones.
|
|
- **Compose:** the app service MUST publish its port as `ports: ["${VCH_PORT}:<internal>"]`. VCH sets `VCH_PORT` so it can run an isolated candidate stack during audits without touching your live stack. Add `env_file: [.env]` to any service that needs production env vars — VCH writes your production variables to a `.env` file next to your compose file.
|
|
- **VCH runs exactly ONE compose file — no `-f` override chains.** VCH auto-discovers a single compose file (`compose.yaml`/`compose.yml`/`docker-compose.yaml`/`docker-compose.yml`) at the repo root **or in a subfolder** (e.g. `docker/`) and runs only that file. A separate override such as `docker-compose.ports.yml` or `*.override.yml` is **ignored** — put the `${VCH_PORT}` port mapping in the *main* compose file, not in an override. If several matching compose files exist, choose one explicitly with `compose: <path>` in `.vch/deploy.yaml`.
|
|
- **The whole compose starts.** VCH brings up the entire compose project, so DB/cache/auth services your compose defines itself (Postgres, Redis, etc.) start automatically — you do **not** also need a separate managed database for them. (Services behind a compose `profiles:` key stay off unless you activate the profile.)
|
|
- If you omit `.vch/deploy.yaml`, VCH falls back to the Dockerfile `EXPOSE` port (or `3000`).
|