chore: update skills 23763b65
Steve · 2026-04-25 22:14 1 file(s) · +163 −44
skills/andromeda-stack/SKILL.md +163 −44
9 9
10 10
Scaffold and build Rust CRUD web applications in the andromeda workspace using Axum + SQLite + Askama templates + `andromeda-auth` for authentication. The result is a single binary web server with HTML pages, a JSON API, optional session or API key auth, and Docker deployment.
11 11
12 -
All apps live under `apps/` in the workspace and share dependencies via the root `Cargo.toml`. The `andromeda-auth` crate at `crates/auth/` provides shared authentication primitives.
12 +
All apps live under `apps/` in the workspace and share dependencies via the root `Cargo.toml`. Shared crates live under `crates/`:
13 +
14 +
- `andromeda-auth` — password verify, session token, cookie helpers
15 +
- `andromeda-db` — DB connection helpers, optional `session` feature
16 +
- `andromeda-darkmatter-css` — shared CSS theme
13 17
14 18
## Project Structure
15 19
58 62
tracing = { workspace = true }
59 63
tracing-subscriber = { workspace = true }
60 64
andromeda-auth = { workspace = true }
65 +
andromeda-db = { workspace = true, features = ["session"] }  # if using session auth
66 +
andromeda-darkmatter-css = { workspace = true }              # if using shared theme
61 67
askama = "0.15"
62 68
askama_web = { version = "0.15", features = ["axum-0.8"] }
63 69
```
64 70
65 -
After creating the app, register it in the workspace root `Cargo.toml` under `[workspace] members`.
71 +
After creating the app, register it in the workspace root `Cargo.toml` under `[workspace] members`. See **Wiring up a new app** below for the full set of workspace files to update.
66 72
67 73
Only add additional crates when the specific app requires them. Do NOT include TUI crates (ratatui, crossterm), CLI crates (clap), or HTTP client crates (reqwest) unless explicitly requested.
68 74
527 533
528 534
## Dockerfile
529 535
530 -
Multi-stage workspace build. Must be built from the repo root with `docker build -f apps/APP_NAME/Dockerfile .`:
536 +
Multi-stage workspace build using `cargo-chef` for dep caching. Built from repo root with `docker build -f apps/APP_NAME/Dockerfile .`:
531 537
532 538
```dockerfile
533 539
# Build from repo root: docker build -t APP_NAME -f apps/APP_NAME/Dockerfile .
534 -
FROM rust:1-slim-bookworm AS builder
535 -
RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/*
540 +
FROM lukemathwalker/cargo-chef:latest-rust-1-slim-bookworm AS chef
536 541
WORKDIR /app
537 542
538 -
# Copy workspace manifests for dependency caching
539 -
COPY Cargo.toml Cargo.lock .
540 -
COPY crates/auth/Cargo.toml crates/auth/
541 -
# Copy all app Cargo.tomls (needed for workspace resolution)
542 -
COPY apps/sipp/Cargo.toml apps/sipp/
543 -
COPY apps/feeds/Cargo.toml apps/feeds/
544 -
COPY apps/parcels/Cargo.toml apps/parcels/
545 -
COPY apps/jotts/Cargo.toml apps/jotts/
546 -
COPY apps/og/Cargo.toml apps/og/
547 -
COPY apps/shrink/Cargo.toml apps/shrink/
548 -
COPY apps/APP_NAME/Cargo.toml apps/APP_NAME/
549 -
550 -
# Create stubs for dependency caching
551 -
RUN mkdir -p crates/auth/src && echo '' > crates/auth/src/lib.rs \
552 -
    && for app in sipp feeds parcels jotts og shrink APP_NAME; do \
553 -
         mkdir -p apps/$app/src && echo 'fn main() {}' > apps/$app/src/main.rs; \
554 -
       done
543 +
FROM chef AS planner
544 +
COPY . .
545 +
RUN cargo chef prepare --recipe-path recipe.json
555 546
547 +
FROM chef AS builder
548 +
RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/*
549 +
COPY --from=planner /app/recipe.json recipe.json
550 +
RUN cargo chef cook --release --recipe-path recipe.json -p APP_NAME
551 +
COPY . .
556 552
RUN cargo build --release -p APP_NAME
557 -
558 -
# Copy real source
559 -
COPY crates/auth/src crates/auth/src
560 -
COPY apps/APP_NAME/src apps/APP_NAME/src
561 -
COPY apps/APP_NAME/static apps/APP_NAME/static
562 -
COPY apps/APP_NAME/templates apps/APP_NAME/templates
563 -
564 -
RUN touch apps/APP_NAME/src/*.rs crates/auth/src/*.rs && cargo build --release -p APP_NAME
565 553
566 554
FROM debian:bookworm-slim
567 555
COPY --from=builder /app/target/release/APP_NAME /usr/local/bin/APP_NAME
572 560
CMD ["APP_NAME"]
573 561
```
574 562
575 -
Replace all `APP_NAME` with the actual binary/package name. If the app makes HTTPS requests (e.g., uses reqwest), add `RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*` to the final stage.
563 +
Replace all `APP_NAME` with actual binary/package name. If app makes HTTPS requests (uses reqwest etc.), add `RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*` to final stage.
576 564
577 565
## docker-compose.yml
578 566
607 595
608 596
Always create one with all configurable env vars and sensible comments.
609 597
598 +
## Wiring up a new app
599 +
600 +
A new app is not done when `cargo run -p APP_NAME` works. Several workspace-level files list every app explicitly and must be updated, or CI/release/deploy break silently.
601 +
602 +
### 1. Workspace root `Cargo.toml`
603 +
604 +
Add to `[workspace] members`:
605 +
606 +
```toml
607 +
[workspace]
608 +
members = [
609 +
    "apps/sipp",
610 +
    # ...
611 +
    "apps/APP_NAME",
612 +
    "crates/auth",
613 +
    "crates/db",
614 +
    "crates/darkmatter-css",
615 +
]
616 +
```
617 +
618 +
### 2. `dist-workspace.toml`
619 +
620 +
Add to `[workspace] members` so `cargo dist` builds release binaries + Homebrew formula:
621 +
622 +
```toml
623 +
[workspace]
624 +
members = [
625 +
    "cargo:apps/sipp",
626 +
    # ...
627 +
    "cargo:apps/APP_NAME",
628 +
]
629 +
```
630 +
631 +
### 3. `.github/workflows/docker.yml`
632 +
633 +
Two spots — both list every app:
634 +
635 +
```yaml
636 +
ALL='["cellar","sipp","feeds","parcels","jotts","og","shrink","backup","posts","library","APP_NAME"]'
637 +
```
638 +
639 +
```yaml
640 +
for app in cellar sipp feeds parcels jotts og shrink backup posts library APP_NAME; do
641 +
```
642 +
643 +
If the cargo package name differs from directory name, add a case to `pkg_to_dir()`:
644 +
645 +
```bash
646 +
pkg_to_dir() {
647 +
  case "$1" in
648 +
    sipp-so) echo "sipp" ;;
649 +
    APP_PKG) echo "APP_NAME" ;;
650 +
    *) echo "$1" ;;
651 +
  esac
652 +
}
653 +
```
654 +
655 +
### 4. `.github/workflows/docker-test.yml`
656 +
657 +
Same two lists — keep in sync with `docker.yml`:
658 +
659 +
```yaml
660 +
ALL='["cellar","sipp","feeds","parcels","jotts","og","shrink","backup","posts","library","APP_NAME"]'
661 +
```
662 +
663 +
```yaml
664 +
for app in cellar sipp feeds parcels jotts og shrink backup posts library APP_NAME; do
665 +
```
666 +
667 +
### 5. Root `docker-compose.yml`
668 +
669 +
Production compose at repo root pulls images from GHCR. Add a service + volume:
670 +
671 +
```yaml
672 +
services:
673 +
  APP_NAME:
674 +
    image: ghcr.io/stevedylandev/andromeda/APP_NAME:latest
675 +
    restart: unless-stopped
676 +
    ports:
677 +
      - "PORT:3000"
678 +
    volumes:
679 +
      - APP_NAME_data:/data
680 +
    env_file: apps/APP_NAME/.env
681 +
682 +
volumes:
683 +
  APP_NAME_data:
684 +
    external: true
685 +
    name: APP_NAME_APP_NAME-data
686 +
```
687 +
688 +
Pick a unique host port (existing: 3000 sipp, 3030 jotts, 3456 og, 3535 posts, 3565 shrink, 4555 feeds, 4646 library, 6754 cellar). Drop `volumes`/`env_file` if app is stateless (see `shrink`, `og`).
689 +
690 +
If the app holds data worth backing up, add a read-only mount to the `backup` service:
691 +
692 +
```yaml
693 +
backup:
694 +
  volumes:
695 +
    - APP_NAME_data:/data/APP_NAME:ro
696 +
```
697 +
698 +
### 6. App-local `docker-compose.yml`
699 +
700 +
Per-app compose for local dev — builds from source:
701 +
702 +
```yaml
703 +
services:
704 +
  app:
705 +
    build:
706 +
      context: ../..
707 +
      dockerfile: apps/APP_NAME/Dockerfile
708 +
    ports:
709 +
      - "${PORT:-3000}:${PORT:-3000}"
710 +
    environment:
711 +
      - APP_PASSWORD=${APP_PASSWORD:-changeme}
712 +
      - APP_DB_PATH=/data/APP_NAME.sqlite
713 +
      - COOKIE_SECURE=false
714 +
      - HOST=0.0.0.0
715 +
      - PORT=${PORT:-3000}
716 +
    volumes:
717 +
      - APP_NAME-data:/data
718 +
    restart: unless-stopped
719 +
720 +
volumes:
721 +
  APP_NAME-data:
722 +
```
723 +
610 724
## Checklist
611 725
612 -
When scaffolding a new app with this pattern:
726 +
When scaffolding a new app:
613 727
614 -
1. Create `apps/app-name/` with `cargo init`
615 -
2. Register in workspace root `Cargo.toml` under `[workspace] members`
616 -
3. Set up `Cargo.toml` with workspace dependencies + `andromeda-auth`
617 -
4. Write `db.rs` — schema, model struct, CRUD functions, session table if needed
618 -
5. Write `auth.rs` — wrap `andromeda-auth` with `AuthSession` extractor (if auth needed)
619 -
6. Write `server.rs` — config, state, templates, handlers, routes
620 -
7. Write `main.rs` — minimal entry point
621 -
8. Create `templates/` with at least a `base.html` and index page
622 -
9. Create `static/styles.css`
623 -
10. Create `.env.example`
624 -
11. Create `Dockerfile` (workspace-aware multi-stage) and `docker-compose.yml`
625 -
12. Test: `cargo run -p app-name`, verify routes work
728 +
1. Create `apps/APP_NAME/` with `cargo init`
729 +
2. Set up `apps/APP_NAME/Cargo.toml` with workspace deps + `andromeda-auth` (and `andromeda-db`, `andromeda-darkmatter-css` if used)
730 +
3. Register app in workspace root `Cargo.toml` under `[workspace] members`
731 +
4. Register app in `dist-workspace.toml` under `[workspace] members` (with `cargo:` prefix)
732 +
5. Add app to `ALL` array + `for app in ...` loop in `.github/workflows/docker.yml`
733 +
6. Add app to `ALL` array + `for app in ...` loop in `.github/workflows/docker-test.yml`
734 +
7. Write `db.rs` — schema, model, CRUD, session table if needed
735 +
8. Write `auth.rs` — wrap `andromeda-auth` with `AuthSession` extractor (if auth needed)
736 +
9. Write `server.rs` — config, state, templates, handlers, routes
737 +
10. Write `main.rs` — minimal entry point
738 +
11. Create `templates/` with `base.html` + index page
739 +
12. Create `static/styles.css`
740 +
13. Create `.env.example`
741 +
14. Create `Dockerfile` (cargo-chef multi-stage) + app-local `docker-compose.yml`
742 +
15. Add service entry + volume to root `docker-compose.yml` (and `backup` mount if stateful)
743 +
16. Test: `cargo run -p APP_NAME`, hit routes
744 +
17. Test: `docker build -f apps/APP_NAME/Dockerfile .` from repo root
626 745
627 746
## What NOT to include
628 747