-
Notifications
You must be signed in to change notification settings - Fork 1
feat: "boring baseline" app template — auth/RBAC + DB + CRUD (#3) #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,6 +19,12 @@ variable "docker_socket" { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type = string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variable "starter_template" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default = "none" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description = "Starter app template to bootstrap in new workspaces. Options: none, fullstack-baseline" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type = string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| provider "docker" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| host = var.docker_socket != "" ? var.docker_socket : null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -55,6 +61,35 @@ resource "coder_agent" "main" { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "[remotevibe] WARNING: $AGENT_ENV not found — AI agents may not be authenticated" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── Bootstrap starter app template ────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| STARTER_TEMPLATE="$${STARTER_TEMPLATE:-none}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "$STARTER_TEMPLATE" = "fullstack-baseline" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TMPL_DIR="/home/coder/fullstack-baseline" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ ! -f "$TMPL_DIR/.bootstrapped" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "[remotevibe] Bootstrapping fullstack-baseline (tiangolo/full-stack-fastapi-template)…" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| git clone --depth=1 https://github.com/tiangolo/full-stack-fastapi-template "$TMPL_DIR" 2>&1 | sed 's/^/[remotevibe] /' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cd "$TMPL_DIR" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Generate .env from the example — patch project name and secret key | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cp .env "$TMPL_DIR/.env.bak" 2>/dev/null || true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sed \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -e "s|^PROJECT_NAME=.*|PROJECT_NAME=fullstack-baseline|" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -e "s|^SECRET_KEY=.*|SECRET_KEY=$(openssl rand -hex 32)|" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -e "s|^FIRST_SUPERUSER=.*|FIRST_SUPERUSER=admin@example.com|" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -e "s|^FIRST_SUPERUSER_PASSWORD=.*|FIRST_SUPERUSER_PASSWORD=changeme123|" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .env > .env.patched && mv .env.patched .env | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docker compose up -d 2>&1 | sed 's/^/[remotevibe] /' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| touch "$TMPL_DIR/.bootstrapped" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+83
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "[remotevibe] fullstack-baseline stack started." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "[remotevibe] Frontend : http://localhost:5173" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "[remotevibe] API docs : http://localhost:8000/docs" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+87
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| git clone --depth=1 https://github.com/tiangolo/full-stack-fastapi-template "$TMPL_DIR" 2>&1 | sed 's/^/[remotevibe] /' | |
| cd "$TMPL_DIR" | |
| # Generate .env from the example — patch project name and secret key | |
| cp .env "$TMPL_DIR/.env.bak" 2>/dev/null || true | |
| sed \ | |
| -e "s|^PROJECT_NAME=.*|PROJECT_NAME=fullstack-baseline|" \ | |
| -e "s|^SECRET_KEY=.*|SECRET_KEY=$(openssl rand -hex 32)|" \ | |
| -e "s|^FIRST_SUPERUSER=.*|FIRST_SUPERUSER=admin@example.com|" \ | |
| -e "s|^FIRST_SUPERUSER_PASSWORD=.*|FIRST_SUPERUSER_PASSWORD=changeme123|" \ | |
| .env > .env.patched && mv .env.patched .env | |
| docker compose up -d 2>&1 | sed 's/^/[remotevibe] /' | |
| touch "$TMPL_DIR/.bootstrapped" | |
| echo "[remotevibe] fullstack-baseline stack started." | |
| echo "[remotevibe] Frontend : http://localhost:5173" | |
| echo "[remotevibe] API docs : http://localhost:8000/docs" | |
| if git clone --depth=1 https://github.com/tiangolo/full-stack-fastapi-template "$TMPL_DIR" 2>&1 | sed 's/^/[remotevibe] /' \ | |
| && cd "$TMPL_DIR" \ | |
| && cp .env "$TMPL_DIR/.env.bak" 2>/dev/null || true \ | |
| && sed \ | |
| -e "s|^PROJECT_NAME=.*|PROJECT_NAME=fullstack-baseline|" \ | |
| -e "s|^SECRET_KEY=.*|SECRET_KEY=$(openssl rand -hex 32)|" \ | |
| -e "s|^FIRST_SUPERUSER=.*|FIRST_SUPERUSER=admin@example.com|" \ | |
| -e "s|^FIRST_SUPERUSER_PASSWORD=.*|FIRST_SUPERUSER_PASSWORD=changeme123|" \ | |
| .env > .env.patched \ | |
| && mv .env.patched .env \ | |
| && docker compose up -d 2>&1 | sed 's/^/[remotevibe] /'; then | |
| touch "$TMPL_DIR/.bootstrapped" | |
| echo "[remotevibe] fullstack-baseline stack started." | |
| echo "[remotevibe] Frontend : http://localhost:5173" | |
| echo "[remotevibe] API docs : http://localhost:8000/docs" | |
| else | |
| echo "[remotevibe] WARNING: fullstack-baseline bootstrap failed; continuing workspace startup without starter stack." | |
| fi |
Copilot
AI
Apr 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The startup script runs docker compose up -d inside the workspace container, but this template does not mount a Docker socket and the workspace image only installs the Docker CLI (no daemon). As a result, docker compose will fail to connect and the starter template bootstrap won’t work. Consider either mounting /var/run/docker.sock (and documenting the security tradeoff) or implementing the stack without requiring Docker-in-workspace (e.g., run services as additional Terraform-managed containers).
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -578,6 +578,52 @@ def _ask_agents(config: dict[str, Any]) -> None: | |||||
| _success("Reusing Google API key already provided.") | ||||||
|
|
||||||
|
|
||||||
| # --------------------------------------------------------------------------- | ||||||
| # Step 4b — Starter app template | ||||||
|
||||||
| # Step 4b — Starter app template | |
| # Step 5b — Starter app template |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -81,7 +81,7 @@ def validate_coder_password(value: str) -> str | bool: | |||||||||||
| return True | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def validate_api_key_optional(value: str) -> str | bool: | ||||||||||||
| """Accept empty or any non-whitespace string.""" | ||||||||||||
|
||||||||||||
| """Accept empty or any non-whitespace string.""" | |
| """Accept empty or any non-whitespace string.""" | |
| value = value.strip() | |
| if not value: | |
| return True |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -204,6 +204,59 @@ which codex # if ENABLE_AGENT_CODEX=true | |
| which opencode # if ENABLE_AGENT_OPENCODE=true | ||
| ``` | ||
|
|
||
| ## Step 7: (Optional) Starter App Templates | ||
|
|
||
| RemoteVibeServer can automatically bootstrap a production-ready starter | ||
| application inside every new Coder workspace. | ||
|
|
||
| ### Available templates | ||
|
|
||
| | Value | Description | | ||
| |---------------------|------------------------------------------------------------| | ||
| | `none` | No template — plain workspace (default) | | ||
| | `fullstack-baseline`| FastAPI + PostgreSQL + React/Vite + Auth + RBAC (tiangolo) | | ||
|
|
||
| ### Enabling a template | ||
|
|
||
| Set `STARTER_TEMPLATE` in `/etc/dev-server/env` **before** provisioning: | ||
|
|
||
| ```yaml | ||
| # In RVSconfig.yml / cloud-init.yaml: | ||
| STARTER_TEMPLATE: "fullstack-baseline" | ||
| ``` | ||
|
Comment on lines
+221
to
+226
|
||
|
|
||
| Or via the configurator CLI — a new step `Starter App Template` appears after | ||
| the AI Agents step. | ||
|
|
||
| ### What fullstack-baseline provides | ||
|
|
||
| Based on **[tiangolo/full-stack-fastapi-template](https://github.com/tiangolo/full-stack-fastapi-template)**: | ||
|
|
||
| - **FastAPI** backend (Python, SQLModel, Alembic migrations) | ||
| - **PostgreSQL 16** database | ||
| - **React + Vite** frontend (TypeScript, Chakra UI) | ||
| - JWT auth + RBAC (superuser / regular-user roles) | ||
| - Docker Compose stack — starts automatically on workspace launch | ||
|
|
||
| **Default ports inside the workspace:** | ||
|
|
||
| | Service | URL | | ||
| |------------------|-----------------------------------| | ||
| | Frontend | http://localhost:5173 | | ||
| | Backend API | http://localhost:8000 | | ||
| | Interactive docs | http://localhost:8000/docs | | ||
|
|
||
| **Default credentials:** `admin@example.com` / `changeme123` (change on first login) | ||
|
|
||
| ### Template lifecycle | ||
|
|
||
| - The stack is cloned and started on the **first workspace launch** (~2 min) | ||
| - Subsequent starts run `docker compose up -d` (fast — containers already exist) | ||
| - A `.bootstrapped` marker prevents re-cloning; local edits persist across restarts | ||
| - Remove `.bootstrapped` and restart the workspace to reset to a clean clone | ||
|
|
||
| For full documentation see [`templates/fullstack-baseline/README.md`](../../templates/fullstack-baseline/README.md). | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Provisioning failed | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # fullstack-baseline — Starter App Template | ||
|
|
||
| A production-ready fullstack application that is automatically bootstrapped | ||
| inside every new Coder workspace when `STARTER_TEMPLATE=fullstack-baseline` is | ||
| configured. | ||
|
|
||
| It is based on **[tiangolo/full-stack-fastapi-template](https://github.com/tiangolo/full-stack-fastapi-template)** | ||
| — the official FastAPI project template maintained by the FastAPI author. | ||
|
|
||
| --- | ||
|
|
||
| ## What you get | ||
|
|
||
| | Layer | Technology | | ||
| |-------------|-----------------------------------------------| | ||
| | Backend API | FastAPI (Python) + SQLModel ORM | | ||
| | Database | PostgreSQL 16 | | ||
| | Migrations | Alembic | | ||
| | Frontend | React + Vite + TypeScript + Chakra UI | | ||
| | Auth | JWT bearer tokens (login / refresh) | | ||
| | RBAC | Superuser + regular-user roles | | ||
| | CRUD | Items resource (scaffold for your own models) | | ||
| | Dev stack | Docker Compose | | ||
|
|
||
| --- | ||
|
|
||
| ## Ports (inside the workspace) | ||
|
|
||
| | Service | URL | | ||
| |------------------|-----------------------------------| | ||
| | Frontend | http://localhost:5173 | | ||
| | Backend API | http://localhost:8000 | | ||
| | Interactive docs | http://localhost:8000/docs | | ||
| | Adminer (DB UI) | http://localhost:8080 | | ||
|
|
||
| --- | ||
|
|
||
| ## Default credentials | ||
|
|
||
| | Field | Value | | ||
| |----------|------------------------| | ||
| | Email | `admin@example.com` | | ||
| | Password | `changeme123` | | ||
|
|
||
| Change the password immediately after first login. | ||
|
|
||
| --- | ||
|
|
||
| ## How it works | ||
|
|
||
| When a workspace starts with `STARTER_TEMPLATE=fullstack-baseline`, the | ||
| `startup_script` in `coder/main.tf`: | ||
|
|
||
| 1. Clones `tiangolo/full-stack-fastapi-template` into `~/fullstack-baseline` | ||
| 2. Patches `.env` (project name, random `SECRET_KEY`, default superuser) | ||
| 3. Runs `docker compose up -d` | ||
| 4. Creates `~/fullstack-baseline/.bootstrapped` to make subsequent starts | ||
| idempotent | ||
|
|
||
| The stack is started on every workspace launch (via `docker compose up -d`), | ||
| and stopped automatically when the workspace is stopped. | ||
|
|
||
| --- | ||
|
|
||
| ## Re-running the bootstrap | ||
|
|
||
| If the bootstrap was interrupted or you want a clean slate: | ||
|
|
||
| ```bash | ||
| rm ~/fullstack-baseline/.bootstrapped | ||
| # restart the workspace, or run the startup script manually | ||
| ``` | ||
|
|
||
| ## Customising | ||
|
|
||
| Fork the template or edit files directly in `~/fullstack-baseline`. The | ||
| `.bootstrapped` marker prevents re-cloning, so local changes persist across | ||
| workspace restarts. | ||
|
|
||
| For a clean re-clone: | ||
|
|
||
| ```bash | ||
| rm -rf ~/fullstack-baseline | ||
| # restart the workspace | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The starter-template block is in the main
startup_script, so withset -ea failedgit clonecan stop the entire agent startup beforecode-serveris launched. In the common failure path where GitHub is temporarily unreachable (or clone fails before the target dir exists), the nextcd "$TMPL_DIR"exits non-zero and the workspace fails to come up even though this feature is optional. Wrapping clone/bootstrap in a non-fatal guard (log + continue) would avoid taking down the whole workspace.Useful? React with 👍 / 👎.