Merge pull request #50 from stevedylandev/chore/rename-go-pkgs 09917d63
Steve Simkins · 2026-05-20 20:39 130 file(s) · +286 −286
.github/workflows/ci.yml +3 −3
18 18
          go-version: '1.25.x'
19 19
          cache-dependency-path: |
20 20
            apps/*/go.sum
21 -
            crates-go/*/go.sum
21 +
            pkg/*/go.sum
22 22
23 23
      - name: go test all modules
24 24
        run: |
25 25
          set -euo pipefail
26 -
          for mod in crates-go/* apps/*; do
26 +
          for mod in pkg/* apps/*; do
27 27
            if [ -f "$mod/go.mod" ]; then
28 28
              echo "::group::$mod"
29 29
              (cd "$mod" && go test ./...)
34 34
      - name: go vet all modules
35 35
        run: |
36 36
          set -euo pipefail
37 -
          for mod in crates-go/* apps/*; do
37 +
          for mod in pkg/* apps/*; do
38 38
            if [ -f "$mod/go.mod" ]; then
39 39
              echo "::group::$mod"
40 40
              (cd "$mod" && go vet ./...)
.github/workflows/docker-test.yml +1 −1
23 23
24 24
          changed=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
25 25
26 -
          if echo "$changed" | grep -qE '^(crates-go/|\.github/workflows/docker(-test)?\.yml)'; then
26 +
          if echo "$changed" | grep -qE '^(pkg/|\.github/workflows/docker(-test)?\.yml)'; then
27 27
            echo "apps=${ALL}" >> "$GITHUB_OUTPUT"
28 28
            exit 0
29 29
          fi
.github/workflows/docker.yml +1 −1
46 46
          changed=$(git diff --name-only HEAD~1 HEAD)
47 47
48 48
          # Workspace-level changes (shared deps, CI) rebuild all
49 -
          if echo "$changed" | grep -qE '^(crates-go/|\.github/workflows/docker\.yml)'; then
49 +
          if echo "$changed" | grep -qE '^(pkg/|\.github/workflows/docker\.yml)'; then
50 50
            echo "apps=${ALL}" >> "$GITHUB_OUTPUT"
51 51
            exit 0
52 52
          fi
README.md +5 −5
23 23
24 24
## Shared packages
25 25
26 -
Under `crates-go/`, each its own Go module:
26 +
Under `pkg/`, each its own Go module:
27 27
28 28
| Package | Description |
29 29
|---|---|
30 -
| `crates-go/web` | HTTP helpers (embedded assets, JSON, render, redirect) |
31 -
| `crates-go/auth` | Sessions store, password/api-key verification, short-id |
32 -
| `crates-go/config` | env + `.env` loading helpers |
33 -
| `crates-go/darkmatter` | Embedded CSS + fonts, mountable on any `http.ServeMux` |
30 +
| `pkg/web` | HTTP helpers (embedded assets, JSON, render, redirect) |
31 +
| `pkg/auth` | Sessions store, password/api-key verification, short-id |
32 +
| `pkg/config` | env + `.env` loading helpers |
33 +
| `pkg/darkmatter` | Embedded CSS + fonts, mountable on any `http.ServeMux` |
34 34
35 35
Each app references these via `replace` directives in its `go.mod`, so the
36 36
source tree is fully self-contained.
apps/bookmarks/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t bookmarks -f apps/bookmarks/Dockerfile .
2 2
FROM golang:1.24-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/bookmarks/go.mod apps/bookmarks/go.sum ./apps/bookmarks/
6 6
WORKDIR /app/apps/bookmarks
7 7
RUN go mod download
apps/bookmarks/app.go +1 −1
6 6
	"html/template"
7 7
	"log/slog"
8 8
9 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/auth"
10 10
)
11 11
12 12
//go:embed templates/*.html static/*
apps/bookmarks/db.go +1 −1
6 6
	"strings"
7 7
	"time"
8 8
9 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/auth"
10 10
)
11 11
12 12
const schema = `
apps/bookmarks/db_test.go +1 −1
4 4
	"database/sql"
5 5
	"testing"
6 6
7 -
	sharedsqlite "github.com/stevedylandev/andromeda/crates-go/sqlite"
7 +
	sharedsqlite "github.com/stevedylandev/andromeda/pkg/sqlite"
8 8
)
9 9
10 10
func openBookmarksTestDB(t *testing.T) *sql.DB {
apps/bookmarks/go.mod +10 −10
3 3
go 1.24.4
4 4
5 5
require (
6 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
7 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
9 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
10 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
6 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
9 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
10 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
11 11
	golang.org/x/net v0.41.0
12 12
)
13 13
27 27
)
28 28
29 29
replace (
30 -
	github.com/stevedylandev/andromeda/crates-go/auth => ../../crates-go/auth
31 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
32 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
33 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
34 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
30 +
	github.com/stevedylandev/andromeda/pkg/auth => ../../pkg/auth
31 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
32 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
33 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
34 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
35 35
)
apps/bookmarks/handlers_api.go +1 −1
4 4
	"net/http"
5 5
	"strings"
6 6
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
func (a *App) apiListCategories(w http.ResponseWriter, r *http.Request) {
apps/bookmarks/handlers_web.go +2 −2
5 5
	"net/http"
6 6
	"strings"
7 7
8 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 -
	"github.com/stevedylandev/andromeda/crates-go/web"
8 +
	"github.com/stevedylandev/andromeda/pkg/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/web"
10 10
)
11 11
12 12
func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) {
apps/bookmarks/main.go +3 −3
9 9
	"os"
10 10
	"time"
11 11
12 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
13 -
	"github.com/stevedylandev/andromeda/crates-go/config"
14 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
12 +
	"github.com/stevedylandev/andromeda/pkg/auth"
13 +
	"github.com/stevedylandev/andromeda/pkg/config"
14 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
15 15
)
16 16
17 17
func main() {
apps/bookmarks/routes.go +3 −3
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
7 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
8 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/auth"
7 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
8 +
	"github.com/stevedylandev/andromeda/pkg/web"
9 9
)
10 10
11 11
func (a *App) routes() *http.ServeMux {
apps/cellar/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t cellar -f apps/cellar/Dockerfile .
2 2
FROM golang:1.24-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/cellar/go.mod apps/cellar/go.sum ./apps/cellar/
6 6
WORKDIR /app/apps/cellar
7 7
RUN go mod download
apps/cellar/app.go +1 −1
6 6
	"html/template"
7 7
	"log/slog"
8 8
9 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/auth"
10 10
)
11 11
12 12
//go:embed templates/*.html static/*
apps/cellar/db.go +1 −1
4 4
	"database/sql"
5 5
	"errors"
6 6
7 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
7 +
	"github.com/stevedylandev/andromeda/pkg/auth"
8 8
)
9 9
10 10
const cellarSchema = `
apps/cellar/db_test.go +1 −1
5 5
	"database/sql"
6 6
	"testing"
7 7
8 -
	sharedsqlite "github.com/stevedylandev/andromeda/crates-go/sqlite"
8 +
	sharedsqlite "github.com/stevedylandev/andromeda/pkg/sqlite"
9 9
)
10 10
11 11
func openCellarTestDB(t *testing.T) *sql.DB {
apps/cellar/go.mod +10 −10
3 3
go 1.24.4
4 4
5 5
require (
6 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
7 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
9 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
10 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
6 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
9 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
10 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
11 11
)
12 12
13 13
require (
26 26
)
27 27
28 28
replace (
29 -
	github.com/stevedylandev/andromeda/crates-go/auth => ../../crates-go/auth
30 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
31 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
32 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
33 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
29 +
	github.com/stevedylandev/andromeda/pkg/auth => ../../pkg/auth
30 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
31 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
32 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
33 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
34 34
)
apps/cellar/handlers_admin.go +2 −2
5 5
	"net/http"
6 6
	"net/url"
7 7
8 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 -
	"github.com/stevedylandev/andromeda/crates-go/web"
8 +
	"github.com/stevedylandev/andromeda/pkg/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/web"
10 10
)
11 11
12 12
func (a *App) loginGet(w http.ResponseWriter, r *http.Request) {
apps/cellar/handlers_api.go +1 −1
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/web"
7 7
)
8 8
9 9
func (a *App) apiListWines(w http.ResponseWriter, r *http.Request) {
apps/cellar/main.go +3 −3
7 7
	"os"
8 8
	"strings"
9 9
10 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
11 -
	"github.com/stevedylandev/andromeda/crates-go/config"
12 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
10 +
	"github.com/stevedylandev/andromeda/pkg/auth"
11 +
	"github.com/stevedylandev/andromeda/pkg/config"
12 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
13 13
)
14 14
15 15
func main() {
apps/cellar/render.go +1 −1
8 8
	"path"
9 9
	"strings"
10 10
11 -
	"github.com/stevedylandev/andromeda/crates-go/web"
11 +
	"github.com/stevedylandev/andromeda/pkg/web"
12 12
)
13 13
14 14
func buildTemplates() (map[string]*template.Template, error) {
apps/cellar/routes.go +2 −2
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
func (a *App) routes() *http.ServeMux {
apps/easel/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t easel -f apps/easel/Dockerfile .
2 2
FROM golang:1.24-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/easel/go.mod apps/easel/go.sum ./apps/easel/
6 6
WORKDIR /app/apps/easel
7 7
RUN go mod download
apps/easel/db_test.go +1 −1
6 6
	"testing"
7 7
	"time"
8 8
9 -
	sharedsqlite "github.com/stevedylandev/andromeda/crates-go/sqlite"
9 +
	sharedsqlite "github.com/stevedylandev/andromeda/pkg/sqlite"
10 10
)
11 11
12 12
func openEaselTestDB(t *testing.T) *sql.DB {
apps/easel/go.mod +8 −8
3 3
go 1.24.4
4 4
5 5
require (
6 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
7 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
9 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
6 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
9 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
10 10
)
11 11
12 12
require (
24 24
)
25 25
26 26
replace (
27 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
28 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
29 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
30 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
27 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
28 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
29 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
30 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
31 31
)
apps/easel/handlers_api.go +1 −1
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/web"
7 7
)
8 8
9 9
type apiArtwork struct {
apps/easel/main.go +2 −2
9 9
	"strings"
10 10
	"time"
11 11
12 -
	"github.com/stevedylandev/andromeda/crates-go/config"
13 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
12 +
	"github.com/stevedylandev/andromeda/pkg/config"
13 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
14 14
)
15 15
16 16
func splitCommaTrim(s string) []string {
apps/easel/routes.go +2 −2
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
func (a *App) routes() *http.ServeMux {
apps/feeds/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t feeds -f apps/feeds/Dockerfile .
2 2
FROM golang:1.25-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/feeds/go.mod apps/feeds/go.sum ./apps/feeds/
6 6
WORKDIR /app/apps/feeds
7 7
RUN go mod download
apps/feeds/app.go +1 −1
6 6
	"html/template"
7 7
	"log/slog"
8 8
9 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/auth"
10 10
)
11 11
12 12
//go:embed templates/*.html static/*
apps/feeds/feeds_test.go +1 −1
9 9
	"strings"
10 10
	"testing"
11 11
12 -
	sharedsqlite "github.com/stevedylandev/andromeda/crates-go/sqlite"
12 +
	sharedsqlite "github.com/stevedylandev/andromeda/pkg/sqlite"
13 13
)
14 14
15 15
func newTestDB(t *testing.T) *sql.DB {
apps/feeds/go.mod +10 −10
4 4
5 5
require (
6 6
	github.com/mmcdole/gofeed v1.3.0
7 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
9 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
10 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
11 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
9 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
10 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
11 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
12 12
	golang.org/x/crypto/x509roots/fallback v0.0.0-20260511143831-44decbfe70e2
13 13
	golang.org/x/net v0.41.0
14 14
)
15 15
16 16
replace (
17 -
	github.com/stevedylandev/andromeda/crates-go/auth => ../../crates-go/auth
18 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
19 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
20 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
21 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
17 +
	github.com/stevedylandev/andromeda/pkg/auth => ../../pkg/auth
18 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
19 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
20 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
21 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
22 22
)
23 23
24 24
require (
apps/feeds/handlers_admin.go +2 −2
5 5
	"net/http"
6 6
	"strings"
7 7
8 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 -
	"github.com/stevedylandev/andromeda/crates-go/web"
8 +
	"github.com/stevedylandev/andromeda/pkg/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/web"
10 10
)
11 11
12 12
func (a *App) loginGetHandler(w http.ResponseWriter, r *http.Request) {
apps/feeds/handlers_api.go +1 −1
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/web"
7 7
)
8 8
9 9
func (a *App) listItemsAPI(w http.ResponseWriter, r *http.Request) {
apps/feeds/handlers_public.go +1 −1
8 8
	"strings"
9 9
	"time"
10 10
11 -
	"github.com/stevedylandev/andromeda/crates-go/web"
11 +
	"github.com/stevedylandev/andromeda/pkg/web"
12 12
)
13 13
14 14
const opmlPreviewPerFeed = 5
apps/feeds/main.go +3 −3
8 8
	"net/http"
9 9
	"os"
10 10
11 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
12 -
	"github.com/stevedylandev/andromeda/crates-go/config"
13 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
11 +
	"github.com/stevedylandev/andromeda/pkg/auth"
12 +
	"github.com/stevedylandev/andromeda/pkg/config"
13 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
14 14
15 15
	_ "golang.org/x/crypto/x509roots/fallback"
16 16
)
apps/feeds/routes.go +3 −3
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
7 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
8 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/auth"
7 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
8 +
	"github.com/stevedylandev/andromeda/pkg/web"
9 9
)
10 10
11 11
func (a *App) routes() *http.ServeMux {
apps/jotts/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t jotts -f apps/jotts/Dockerfile .
2 2
FROM golang:1.25-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/jotts/go.mod apps/jotts/go.sum ./apps/jotts/
6 6
WORKDIR /app/apps/jotts
7 7
RUN go mod download
apps/jotts/app.go +1 −1
7 7
	"log/slog"
8 8
9 9
	"github.com/stevedylandev/andromeda/apps/jotts/internal/store"
10 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
10 +
	"github.com/stevedylandev/andromeda/pkg/auth"
11 11
)
12 12
13 13
//go:embed templates/*.html static/*
apps/jotts/cmd_server.go +2 −2
7 7
	"net/http"
8 8
	"os"
9 9
10 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
11 -
	"github.com/stevedylandev/andromeda/crates-go/config"
10 +
	"github.com/stevedylandev/andromeda/pkg/auth"
11 +
	"github.com/stevedylandev/andromeda/pkg/config"
12 12
)
13 13
14 14
func runServer(args []string) {
apps/jotts/go.mod +12 −12
9 9
	charm.land/lipgloss/v2 v2.0.3
10 10
	github.com/atotto/clipboard v0.1.4
11 11
	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
12 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
13 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
14 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
15 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
16 -
	github.com/stevedylandev/andromeda/crates-go/tui v0.0.0
17 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
12 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
13 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
14 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
15 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
16 +
	github.com/stevedylandev/andromeda/pkg/tui v0.0.0
17 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
18 18
	github.com/yuin/goldmark v1.7.13
19 19
	golang.org/x/term v0.43.0
20 20
)
21 21
22 22
replace (
23 -
	github.com/stevedylandev/andromeda/crates-go/auth => ../../crates-go/auth
24 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
25 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
26 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
27 -
	github.com/stevedylandev/andromeda/crates-go/tui => ../../crates-go/tui
28 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
23 +
	github.com/stevedylandev/andromeda/pkg/auth => ../../pkg/auth
24 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
25 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
26 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
27 +
	github.com/stevedylandev/andromeda/pkg/tui => ../../pkg/tui
28 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
29 29
)
30 30
31 31
require (
apps/jotts/handlers_api.go +1 −1
4 4
	"net/http"
5 5
	"strings"
6 6
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
func (a *App) apiListNotes(w http.ResponseWriter, r *http.Request) {
apps/jotts/handlers_web.go +2 −2
5 5
	"net/http"
6 6
	"strings"
7 7
8 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 -
	"github.com/stevedylandev/andromeda/crates-go/web"
8 +
	"github.com/stevedylandev/andromeda/pkg/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/web"
10 10
)
11 11
12 12
func (a *App) loginGetHandler(w http.ResponseWriter, r *http.Request) {
apps/jotts/internal/store/store.go +2 −2
4 4
	"database/sql"
5 5
	"errors"
6 6
7 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
8 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
7 +
	"github.com/stevedylandev/andromeda/pkg/auth"
8 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
9 9
)
10 10
11 11
type Note struct {
apps/jotts/routes.go +3 −3
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
7 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
8 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/auth"
7 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
8 +
	"github.com/stevedylandev/andromeda/pkg/web"
9 9
)
10 10
11 11
func (a *App) routes() *http.ServeMux {
apps/jotts/tui/backend.go +1 −1
12 12
	"time"
13 13
14 14
	"github.com/stevedylandev/andromeda/apps/jotts/internal/store"
15 -
	"github.com/stevedylandev/andromeda/crates-go/config"
15 +
	"github.com/stevedylandev/andromeda/pkg/config"
16 16
)
17 17
18 18
type Note = store.Note
apps/jotts/tui/config.go +1 −1
1 1
package tui
2 2
3 -
import sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
3 +
import sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
4 4
5 5
const appName = "jotts"
6 6
apps/jotts/tui/editor.go +1 −1
2 2
3 3
import (
4 4
	tea "charm.land/bubbletea/v2"
5 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
5 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
6 6
)
7 7
8 8
func openExternalEditor(shortID, content string) tea.Cmd {
apps/jotts/tui/keys.go +1 −1
1 1
package tui
2 2
3 -
import sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
3 +
import sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
4 4
5 5
type keyMap = sharedtui.KeyMap
6 6
apps/jotts/tui/list_model.go +1 −1
3 3
import (
4 4
	"charm.land/bubbles/v2/list"
5 5
	tea "charm.land/bubbletea/v2"
6 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
6 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
7 7
)
8 8
9 9
type noteItem struct {
apps/jotts/tui/messages.go +1 −1
1 1
package tui
2 2
3 -
import sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
3 +
import sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
4 4
5 5
type notesLoadedMsg struct {
6 6
	Notes []Note
apps/jotts/tui/update.go +1 −1
6 6
7 7
	"charm.land/bubbles/v2/key"
8 8
	tea "charm.land/bubbletea/v2"
9 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
9 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
10 10
)
11 11
12 12
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
apps/jotts/tui/view.go +1 −1
5 5
6 6
	tea "charm.land/bubbletea/v2"
7 7
	"charm.land/lipgloss/v2"
8 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
8 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
9 9
)
10 10
11 11
var (
apps/library/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t library -f apps/library/Dockerfile .
2 2
FROM golang:1.24-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/library/go.mod apps/library/go.sum ./apps/library/
6 6
WORKDIR /app/apps/library
7 7
RUN go mod download
apps/library/app.go +1 −1
6 6
	"html/template"
7 7
	"log/slog"
8 8
9 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/auth"
10 10
)
11 11
12 12
//go:embed templates/*.html static/*
apps/library/db_test.go +1 −1
4 4
	"database/sql"
5 5
	"testing"
6 6
7 -
	sharedsqlite "github.com/stevedylandev/andromeda/crates-go/sqlite"
7 +
	sharedsqlite "github.com/stevedylandev/andromeda/pkg/sqlite"
8 8
)
9 9
10 10
func openLibraryTestDB(t *testing.T) *sql.DB {
apps/library/go.mod +10 −10
3 3
go 1.24.4
4 4
5 5
require (
6 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
7 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
9 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
10 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
6 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
9 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
10 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
11 11
)
12 12
13 13
require (
26 26
)
27 27
28 28
replace (
29 -
	github.com/stevedylandev/andromeda/crates-go/auth => ../../crates-go/auth
30 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
31 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
32 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
33 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
29 +
	github.com/stevedylandev/andromeda/pkg/auth => ../../pkg/auth
30 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
31 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
32 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
33 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
34 34
)
apps/library/handlers_api.go +1 −1
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/web"
7 7
)
8 8
9 9
func (a *App) apiListBooks(w http.ResponseWriter, r *http.Request) {
apps/library/handlers_web.go +2 −2
5 5
	"strconv"
6 6
	"strings"
7 7
8 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 -
	"github.com/stevedylandev/andromeda/crates-go/web"
8 +
	"github.com/stevedylandev/andromeda/pkg/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/web"
10 10
)
11 11
12 12
var sectionDefs = []struct {
apps/library/main.go +3 −3
7 7
	"net/http"
8 8
	"os"
9 9
10 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
11 -
	"github.com/stevedylandev/andromeda/crates-go/config"
12 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
10 +
	"github.com/stevedylandev/andromeda/pkg/auth"
11 +
	"github.com/stevedylandev/andromeda/pkg/config"
12 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
13 13
)
14 14
15 15
func main() {
apps/library/routes.go +2 −2
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
func (a *App) routes() *http.ServeMux {
apps/og/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t og -f apps/og/Dockerfile .
2 2
FROM golang:1.24-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/og/go.mod apps/og/go.sum ./apps/og/
6 6
WORKDIR /app/apps/og
7 7
RUN go mod download
apps/og/README.md +1 −1
23 23
- `GET /` — search form
24 24
- `POST /check` — inspect a URL (form field: `url`)
25 25
- `GET /static/*` — embedded favicon, styles, etc.
26 -
- `GET /assets/darkmatter.css` + `/assets/fonts/*` — served by `crates-go/darkmatter`
26 +
- `GET /assets/darkmatter.css` + `/assets/fonts/*` — served by `pkg/darkmatter`
27 27
28 28
## Build
29 29
apps/og/go.mod +6 −6
3 3
go 1.24.4
4 4
5 5
require (
6 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
7 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
6 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
9 9
	golang.org/x/net v0.41.0
10 10
)
11 11
12 12
replace (
13 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
14 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
15 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
13 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
14 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
15 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
16 16
)
apps/og/main.go +1 −1
6 6
	"net/http"
7 7
	"os"
8 8
9 -
	"github.com/stevedylandev/andromeda/crates-go/config"
9 +
	"github.com/stevedylandev/andromeda/pkg/config"
10 10
)
11 11
12 12
func main() {
apps/og/render.go +1 −1
8 8
	"path"
9 9
	"strings"
10 10
11 -
	"github.com/stevedylandev/andromeda/crates-go/web"
11 +
	"github.com/stevedylandev/andromeda/pkg/web"
12 12
)
13 13
14 14
func buildTemplates() (map[string]*template.Template, error) {
apps/og/routes.go +2 −2
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
func (a *App) routes() *http.ServeMux {
apps/posts/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t posts -f apps/posts/Dockerfile .
2 2
FROM golang:1.24-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/posts/go.mod apps/posts/go.sum ./apps/posts/
6 6
WORKDIR /app/apps/posts
7 7
RUN go mod download
apps/posts/app.go +1 −1
8 8
	"strings"
9 9
10 10
	poststorage "github.com/stevedylandev/andromeda/apps/posts/storage"
11 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
11 +
	"github.com/stevedylandev/andromeda/pkg/auth"
12 12
)
13 13
14 14
//go:embed templates/*.html static/*
apps/posts/db.go +1 −1
6 6
	"strings"
7 7
	"time"
8 8
9 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
9 +
	"github.com/stevedylandev/andromeda/pkg/auth"
10 10
)
11 11
12 12
const postsSchema = `
apps/posts/db_test.go +1 −1
4 4
	"database/sql"
5 5
	"testing"
6 6
7 -
	sharedsqlite "github.com/stevedylandev/andromeda/crates-go/sqlite"
7 +
	sharedsqlite "github.com/stevedylandev/andromeda/pkg/sqlite"
8 8
)
9 9
10 10
func openPostsTestDB(t *testing.T) *sql.DB {
apps/posts/go.mod +10 −10
3 3
go 1.24.4
4 4
5 5
require (
6 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
7 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
9 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
10 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
6 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
9 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
10 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
11 11
	github.com/yuin/goldmark v1.7.8
12 12
)
13 13
39 39
)
40 40
41 41
replace (
42 -
	github.com/stevedylandev/andromeda/crates-go/auth => ../../crates-go/auth
43 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
44 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
45 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
46 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
42 +
	github.com/stevedylandev/andromeda/pkg/auth => ../../pkg/auth
43 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
44 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
45 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
46 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
47 47
)
apps/posts/handlers_admin.go +1 −1
9 9
	"net/url"
10 10
	"strings"
11 11
12 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
12 +
	"github.com/stevedylandev/andromeda/pkg/auth"
13 13
)
14 14
15 15
const importMaxBytes = 50 * 1024 * 1024
apps/posts/handlers_api.go +1 −1
4 4
	"net/http"
5 5
	"strconv"
6 6
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
const defaultListLimit int64 = 30
apps/posts/main.go +3 −3
8 8
	"strings"
9 9
10 10
	poststorage "github.com/stevedylandev/andromeda/apps/posts/storage"
11 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
12 -
	"github.com/stevedylandev/andromeda/crates-go/config"
13 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
11 +
	"github.com/stevedylandev/andromeda/pkg/auth"
12 +
	"github.com/stevedylandev/andromeda/pkg/config"
13 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
14 14
)
15 15
16 16
func main() {
apps/posts/render.go +1 −1
8 8
	"path"
9 9
	"strings"
10 10
11 -
	"github.com/stevedylandev/andromeda/crates-go/web"
11 +
	"github.com/stevedylandev/andromeda/pkg/web"
12 12
)
13 13
14 14
func buildTemplates() (map[string]*template.Template, error) {
apps/posts/routes.go +2 −2
4 4
	"net/http"
5 5
	"strings"
6 6
7 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
8 -
	"github.com/stevedylandev/andromeda/crates-go/web"
7 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
8 +
	"github.com/stevedylandev/andromeda/pkg/web"
9 9
)
10 10
11 11
func (a *App) routes() *http.ServeMux {
apps/shrink/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t shrink -f apps/shrink/Dockerfile .
2 2
FROM golang:1.24-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/shrink/go.mod apps/shrink/go.sum ./apps/shrink/
6 6
WORKDIR /app/apps/shrink
7 7
RUN go mod download
apps/shrink/go.mod +6 −6
3 3
go 1.24.4
4 4
5 5
require (
6 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
7 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
8 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
6 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
7 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
8 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
9 9
	golang.org/x/image v0.27.0
10 10
)
11 11
12 12
replace (
13 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
14 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
15 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
13 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
14 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
15 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
16 16
)
apps/shrink/handlers.go +1 −1
5 5
	"net/http"
6 6
	"strconv"
7 7
8 -
	"github.com/stevedylandev/andromeda/crates-go/web"
8 +
	"github.com/stevedylandev/andromeda/pkg/web"
9 9
)
10 10
11 11
const maxUploadBytes = 20 * 1024 * 1024
apps/shrink/main.go +1 −1
7 7
	"net/http"
8 8
	"os"
9 9
10 -
	"github.com/stevedylandev/andromeda/crates-go/config"
10 +
	"github.com/stevedylandev/andromeda/pkg/config"
11 11
)
12 12
13 13
func main() {
apps/shrink/routes.go +2 −2
3 3
import (
4 4
	"net/http"
5 5
6 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
7 -
	"github.com/stevedylandev/andromeda/crates-go/web"
6 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
7 +
	"github.com/stevedylandev/andromeda/pkg/web"
8 8
)
9 9
10 10
func (a *App) routes() *http.ServeMux {
apps/sipp/Dockerfile +1 −1
1 1
# Build from repo root: docker build -t sipp -f apps/sipp/Dockerfile .
2 2
FROM golang:1.25-bookworm AS builder
3 3
WORKDIR /app
4 -
COPY crates-go/ ./crates-go/
4 +
COPY pkg/ ./pkg/
5 5
COPY apps/sipp/go.mod apps/sipp/go.sum ./apps/sipp/
6 6
WORKDIR /app/apps/sipp
7 7
RUN go mod download
apps/sipp/go.mod +12 −12
8 8
	charm.land/lipgloss/v2 v2.0.3
9 9
	github.com/alecthomas/chroma/v2 v2.14.0
10 10
	github.com/atotto/clipboard v0.1.4
11 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
12 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
13 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
14 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
15 -
	github.com/stevedylandev/andromeda/crates-go/tui v0.0.0
16 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
11 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
12 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
13 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
14 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
15 +
	github.com/stevedylandev/andromeda/pkg/tui v0.0.0
16 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
17 17
	golang.org/x/term v0.43.0
18 18
)
19 19
51 51
)
52 52
53 53
replace (
54 -
	github.com/stevedylandev/andromeda/crates-go/auth => ../../crates-go/auth
55 -
	github.com/stevedylandev/andromeda/crates-go/config => ../../crates-go/config
56 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
57 -
	github.com/stevedylandev/andromeda/crates-go/sqlite => ../../crates-go/sqlite
58 -
	github.com/stevedylandev/andromeda/crates-go/tui => ../../crates-go/tui
59 -
	github.com/stevedylandev/andromeda/crates-go/web => ../../crates-go/web
54 +
	github.com/stevedylandev/andromeda/pkg/auth => ../../pkg/auth
55 +
	github.com/stevedylandev/andromeda/pkg/config => ../../pkg/config
56 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
57 +
	github.com/stevedylandev/andromeda/pkg/sqlite => ../../pkg/sqlite
58 +
	github.com/stevedylandev/andromeda/pkg/tui => ../../pkg/tui
59 +
	github.com/stevedylandev/andromeda/pkg/web => ../../pkg/web
60 60
)
apps/sipp/internal/store/store.go +2 −2
4 4
	"database/sql"
5 5
	"errors"
6 6
7 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
8 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
7 +
	"github.com/stevedylandev/andromeda/pkg/auth"
8 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
9 9
)
10 10
11 11
type Snippet struct {
apps/sipp/main.go +1 −1
15 15
16 16
	"github.com/stevedylandev/andromeda/apps/sipp/server"
17 17
	"github.com/stevedylandev/andromeda/apps/sipp/tui"
18 -
	"github.com/stevedylandev/andromeda/crates-go/config"
18 +
	"github.com/stevedylandev/andromeda/pkg/config"
19 19
)
20 20
21 21
const usage = `sipp — minimal code sharing CLI
apps/sipp/server/server.go +4 −4
21 21
	"github.com/alecthomas/chroma/v2/lexers"
22 22
	"github.com/alecthomas/chroma/v2/styles"
23 23
	"github.com/stevedylandev/andromeda/apps/sipp/internal/store"
24 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
25 -
	"github.com/stevedylandev/andromeda/crates-go/config"
26 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
27 -
	"github.com/stevedylandev/andromeda/crates-go/web"
24 +
	"github.com/stevedylandev/andromeda/pkg/auth"
25 +
	"github.com/stevedylandev/andromeda/pkg/config"
26 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
27 +
	"github.com/stevedylandev/andromeda/pkg/web"
28 28
)
29 29
30 30
//go:embed templates/*.html static/*
apps/sipp/tui/backend.go +1 −1
12 12
	"time"
13 13
14 14
	"github.com/stevedylandev/andromeda/apps/sipp/internal/store"
15 -
	"github.com/stevedylandev/andromeda/crates-go/config"
15 +
	"github.com/stevedylandev/andromeda/pkg/config"
16 16
)
17 17
18 18
type Snippet = store.Snippet
apps/sipp/tui/config.go +1 −1
1 1
package tui
2 2
3 -
import sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
3 +
import sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
4 4
5 5
const appName = "sipp"
6 6
apps/sipp/tui/editor.go +1 −1
4 4
	"path/filepath"
5 5
6 6
	tea "charm.land/bubbletea/v2"
7 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
7 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
8 8
)
9 9
10 10
func openExternalEditor(shortID, name, content string) tea.Cmd {
apps/sipp/tui/keys.go +1 −1
1 1
package tui
2 2
3 -
import sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
3 +
import sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
4 4
5 5
type keyMap = sharedtui.KeyMap
6 6
apps/sipp/tui/list_model.go +1 −1
3 3
import (
4 4
	"charm.land/bubbles/v2/list"
5 5
	tea "charm.land/bubbletea/v2"
6 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
6 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
7 7
)
8 8
9 9
var listIDStyle = sharedtui.ListIDStyle
apps/sipp/tui/messages.go +1 −1
1 1
package tui
2 2
3 -
import sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
3 +
import sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
4 4
5 5
type snippetsLoadedMsg struct {
6 6
	Snippets []Snippet
apps/sipp/tui/update.go +1 −1
6 6
7 7
	"charm.land/bubbles/v2/key"
8 8
	tea "charm.land/bubbletea/v2"
9 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
9 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
10 10
)
11 11
12 12
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
apps/sipp/tui/view.go +1 −1
5 5
6 6
	tea "charm.land/bubbletea/v2"
7 7
	"charm.land/lipgloss/v2"
8 -
	sharedtui "github.com/stevedylandev/andromeda/crates-go/tui"
8 +
	sharedtui "github.com/stevedylandev/andromeda/pkg/tui"
9 9
)
10 10
11 11
var (
crates-go/auth/auth.go → pkg/auth/auth.go +0 −0
crates-go/auth/auth_test.go → pkg/auth/auth_test.go +0 −0
crates-go/auth/go.mod (deleted) +0 −5
1 -
module github.com/stevedylandev/andromeda/crates-go/auth
2 -
3 -
go 1.24
4 -
5 -
require golang.org/x/crypto v0.39.0
crates-go/auth/go.sum → pkg/auth/go.sum +0 −0
crates-go/config/config.go → pkg/config/config.go +0 −0
crates-go/config/go.mod (deleted) +0 −3
1 -
module github.com/stevedylandev/andromeda/crates-go/config
2 -
3 -
go 1.24
crates-go/darkmatter/assets/darkmatter.css → pkg/darkmatter/assets/darkmatter.css +0 −0
crates-go/darkmatter/assets/fonts/CommitMono-400-Regular.otf → pkg/darkmatter/assets/fonts/CommitMono-400-Regular.otf +0 −0

Binary file — no preview.

crates-go/darkmatter/assets/fonts/CommitMono-700-Regular.otf → pkg/darkmatter/assets/fonts/CommitMono-700-Regular.otf +0 −0

Binary file — no preview.

crates-go/darkmatter/assets/index.html → pkg/darkmatter/assets/index.html +0 −0
crates-go/darkmatter/darkmatter.go → pkg/darkmatter/darkmatter.go +0 −0
crates-go/darkmatter/go.mod (deleted) +0 −3
1 -
module github.com/stevedylandev/andromeda/crates-go/darkmatter
2 -
3 -
go 1.24
crates-go/sqlite/go.mod → pkg/sqlite/go.mod +1 −1
1 -
module github.com/stevedylandev/andromeda/crates-go/sqlite
1 +
module github.com/stevedylandev/andromeda/pkg/sqlite
2 2
3 3
go 1.24
4 4
crates-go/sqlite/go.sum → pkg/sqlite/go.sum +0 −0
crates-go/sqlite/sqlite.go → pkg/sqlite/sqlite.go +0 −0
crates-go/tui/commands.go → pkg/tui/commands.go +0 −0
crates-go/tui/config.go → pkg/tui/config.go +0 −0
crates-go/tui/config_test.go → pkg/tui/config_test.go +0 −0
crates-go/tui/editor.go → pkg/tui/editor.go +0 −0
crates-go/tui/go.mod → pkg/tui/go.mod +1 −1
1 -
module github.com/stevedylandev/andromeda/crates-go/tui
1 +
module github.com/stevedylandev/andromeda/pkg/tui
2 2
3 3
go 1.25.0
4 4
crates-go/tui/go.sum → pkg/tui/go.sum +0 −0
crates-go/tui/keys.go → pkg/tui/keys.go +0 −0
crates-go/tui/list.go → pkg/tui/list.go +0 −0
crates-go/tui/messages.go → pkg/tui/messages.go +0 −0
crates-go/tui/styles.go → pkg/tui/styles.go +0 −0
crates-go/web/go.mod (deleted) +0 −3
1 -
module github.com/stevedylandev/andromeda/crates-go/web
2 -
3 -
go 1.24
crates-go/web/web.go → pkg/web/web.go +0 −0
docs/docs/pages/diy/skills.mdx +2 −2
6 6
7 7
[`darkmatter-styles`](https://github.com/stevedylandev/andromeda/tree/main/skills/darkmatter-styles/SKILL.md) applies the shared Andromeda aesthetic: dark background, white borders, Commit Mono, minimal layout, no frameworks. Use it when building new pages or components that need to match the rest of the lineup.
8 8
9 -
The canonical CSS and fonts live in the [`crates-go/darkmatter`](https://github.com/stevedylandev/andromeda/tree/main/crates-go/darkmatter) module. Mount its handler set and link `/assets/darkmatter.css` from your templates — the skill guides style decisions while the module serves the actual bytes.
9 +
The canonical CSS and fonts live in the [`pkg/darkmatter`](https://github.com/stevedylandev/andromeda/tree/main/pkg/darkmatter) module. Mount its handler set and link `/assets/darkmatter.css` from your templates — the skill guides style decisions while the module serves the actual bytes.
10 10
11 11
## andromeda-stack
12 12
13 -
[`andromeda-stack`](https://github.com/stevedylandev/andromeda/tree/main/skills/andromeda-stack/SKILL.md) scaffolds a new Go CRUD app in the monorepo using the standard project layout: `net/http` mux, `html/template`, `modernc.org/sqlite`, and the `crates-go/{auth,web,config,sqlite,darkmatter}` packages wired in via `replace` directives.
13 +
[`andromeda-stack`](https://github.com/stevedylandev/andromeda/tree/main/skills/andromeda-stack/SKILL.md) scaffolds a new Go CRUD app in the monorepo using the standard project layout: `net/http` mux, `html/template`, `modernc.org/sqlite`, and the `pkg/{auth,web,config,sqlite,darkmatter}` packages wired in via `replace` directives.
14 14
15 15
It covers the full app shape — `main.go` / `app.go` / `routes.go` / `db.go` / `handlers_*.go` / `templates/` / `static/` — plus session vs. API-key vs. bearer auth, the multi-stage `CGO_ENABLED=0` Dockerfile, and the workspace files that need updating (root `docker-compose.yml`, both `.github/workflows/docker*.yml` lists) so a new app actually ships.
16 16
docs/docs/pages/diy/stack.mdx +7 −7
38 38
39 39
## Shared Packages
40 40
41 -
Each is its own Go module under `crates-go/`, referenced via `replace` directives in each app's `go.mod`.
41 +
Each is its own Go module under `pkg/`, referenced via `replace` directives in each app's `go.mod`.
42 42
43 -
### crates-go/auth
43 +
### pkg/auth
44 44
45 45
Provides session-based password authentication used across apps that require login. It handles:
46 46
49 49
- `RequireSession`, `RequireAPIKey`, and `RequireBearerOrSession` middleware
50 50
- Short-id and session-token generators
51 51
52 -
### crates-go/sqlite
52 +
### pkg/sqlite
53 53
54 54
Thin bootstrap around `database/sql` + `modernc.org/sqlite`. `Open(path, schema)` opens the DB with the project defaults (`PRAGMA foreign_keys=ON`, single open connection) and applies an optional schema string on first open.
55 55
56 -
### crates-go/web
56 +
### pkg/web
57 57
58 58
HTTP helpers used across apps:
59 59
62 62
- `EmbeddedHandler` — serve assets out of an `embed.FS`
63 63
- redirect helpers
64 64
65 -
### crates-go/config
65 +
### pkg/config
66 66
67 67
Loads env vars from process environment and `.env` files.
68 68
69 -
### crates-go/darkmatter
69 +
### pkg/darkmatter
70 70
71 71
Ships the canonical Darkmatter stylesheet and Commit Mono fonts as an embedded handler set. Call `Mount()` once on your mux and every app inherits the shared aesthetic:
72 72
75 75
- `/darkmatter` — live component gallery
76 76
77 77
```go
78 -
import "github.com/stevedylandev/andromeda/crates-go/darkmatter"
78 +
import "github.com/stevedylandev/andromeda/pkg/darkmatter"
79 79
80 80
mux := http.NewServeMux()
81 81
darkmatter.Mount(mux, "/assets")
pkg/auth/go.mod (added) +5 −0
1 +
module github.com/stevedylandev/andromeda/pkg/auth
2 +
3 +
go 1.24
4 +
5 +
require golang.org/x/crypto v0.39.0
pkg/config/go.mod (added) +3 −0
1 +
module github.com/stevedylandev/andromeda/pkg/config
2 +
3 +
go 1.24
pkg/darkmatter/go.mod (added) +3 −0
1 +
module github.com/stevedylandev/andromeda/pkg/darkmatter
2 +
3 +
go 1.24
pkg/web/go.mod (added) +3 −0
1 +
module github.com/stevedylandev/andromeda/pkg/web
2 +
3 +
go 1.24
skills/andromeda-stack/SKILL.md +41 −41
1 1
---
2 2
name: Andromeda Stack
3 -
description: Scaffold a Go CRUD web app using net/http + html/template + modernc.org/sqlite + the shared andromeda crates-go packages (auth, web, config, sqlite, darkmatter). Use when the user wants to build a new Go web server with CRUD operations in the andromeda monorepo.
3 +
description: Scaffold a Go CRUD web app using net/http + html/template + modernc.org/sqlite + the shared andromeda pkg packages (auth, web, config, sqlite, darkmatter). Use when the user wants to build a new Go web server with CRUD operations in the andromeda monorepo.
4 4
---
5 5
6 6
# Go Andromeda Web App
9 9
10 10
Scaffold and build Go CRUD web apps in the andromeda workspace using the
11 11
standard library `net/http` mux, `html/template`, `modernc.org/sqlite` (pure
12 -
Go, no cgo) and the shared `crates-go/*` packages. Each app ships a single Go
12 +
Go, no cgo) and the shared `pkg/*` packages. Each app ships a single Go
13 13
binary with HTML pages, a JSON API, optional session or API key auth, embedded
14 14
templates + static assets via `embed.FS`, and Docker deployment.
15 15
16 16
Apps live under `apps/<name>/`. Each app is its own Go module. Shared packages
17 -
live under `crates-go/` and are each their own module, wired in via local
17 +
live under `pkg/` and are each their own module, wired in via local
18 18
`replace` directives.
19 19
20 20
Shared crates:
21 21
22 -
- `crates-go/auth` — session `Store`, `RequireSession` / `RequireAPIKey` /
22 +
- `pkg/auth` — session `Store`, `RequireSession` / `RequireAPIKey` /
23 23
  `RequireBearerOrSession` middleware, `SecureEqual`, `VerifyPassword`
24 24
  (bcrypt or plaintext), `GenerateSessionToken`, `GenerateShortID`.
25 -
- `crates-go/web` — `Render`, `WriteJSON`, `WriteError`, `DecodeJSON`,
25 +
- `pkg/web` — `Render`, `WriteJSON`, `WriteError`, `DecodeJSON`,
26 26
  `EmbeddedHandler`, `RedirectWithError`, `RedirectWithSuccess`, `PathInt64`.
27 -
- `crates-go/config` — `LoadDotEnv`, `Getenv`, `GetenvInt`, `GetenvBool`.
28 -
- `crates-go/sqlite` — `Open(path, schema)` opens SQLite with the project
27 +
- `pkg/config` — `LoadDotEnv`, `Getenv`, `GetenvInt`, `GetenvBool`.
28 +
- `pkg/sqlite` — `Open(path, schema)` opens SQLite with the project
29 29
  defaults (`PRAGMA foreign_keys=ON`, `SetMaxOpenConns(1)`).
30 -
- `crates-go/darkmatter` — embedded shared CSS + fonts plus `Mount(mux,
30 +
- `pkg/darkmatter` — embedded shared CSS + fonts plus `Mount(mux,
31 31
  "/assets")` to register routes on any `*http.ServeMux`.
32 32
33 33
## Project Structure
67 67
go 1.25
68 68
69 69
require (
70 -
	github.com/stevedylandev/andromeda/crates-go/auth v0.0.0
71 -
	github.com/stevedylandev/andromeda/crates-go/config v0.0.0
72 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter v0.0.0
73 -
	github.com/stevedylandev/andromeda/crates-go/sqlite v0.0.0
74 -
	github.com/stevedylandev/andromeda/crates-go/web v0.0.0
70 +
	github.com/stevedylandev/andromeda/pkg/auth v0.0.0
71 +
	github.com/stevedylandev/andromeda/pkg/config v0.0.0
72 +
	github.com/stevedylandev/andromeda/pkg/darkmatter v0.0.0
73 +
	github.com/stevedylandev/andromeda/pkg/sqlite v0.0.0
74 +
	github.com/stevedylandev/andromeda/pkg/web v0.0.0
75 75
)
76 76
77 77
replace (
78 -
	github.com/stevedylandev/andromeda/crates-go/auth       => ../../crates-go/auth
79 -
	github.com/stevedylandev/andromeda/crates-go/config     => ../../crates-go/config
80 -
	github.com/stevedylandev/andromeda/crates-go/darkmatter => ../../crates-go/darkmatter
81 -
	github.com/stevedylandev/andromeda/crates-go/sqlite     => ../../crates-go/sqlite
82 -
	github.com/stevedylandev/andromeda/crates-go/web        => ../../crates-go/web
78 +
	github.com/stevedylandev/andromeda/pkg/auth       => ../../pkg/auth
79 +
	github.com/stevedylandev/andromeda/pkg/config     => ../../pkg/config
80 +
	github.com/stevedylandev/andromeda/pkg/darkmatter => ../../pkg/darkmatter
81 +
	github.com/stevedylandev/andromeda/pkg/sqlite     => ../../pkg/sqlite
82 +
	github.com/stevedylandev/andromeda/pkg/web        => ../../pkg/web
83 83
)
84 84
```
85 85
86 -
Add `crates-go/tui` only if the app ships a TUI. Add `golang.org/x/crypto`,
86 +
Add `pkg/tui` only if the app ships a TUI. Add `golang.org/x/crypto`,
87 87
`yuin/goldmark`, etc. only when the specific app needs them. The Dockerfile
88 -
must copy `crates-go/` into the build context for the replace directives.
88 +
must copy `pkg/` into the build context for the replace directives.
89 89
90 90
Do NOT add ORMs, web frameworks (gin, echo, chi), connection pools, or HTTP
91 91
client libs unless explicitly requested. The stdlib `net/http` mux (with
105 105
	"net/http"
106 106
	"os"
107 107
108 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
109 -
	"github.com/stevedylandev/andromeda/crates-go/config"
108 +
	"github.com/stevedylandev/andromeda/pkg/auth"
109 +
	"github.com/stevedylandev/andromeda/pkg/config"
110 110
)
111 111
112 112
func main() {
176 176
	"html/template"
177 177
	"log/slog"
178 178
179 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
179 +
	"github.com/stevedylandev/andromeda/pkg/auth"
180 180
)
181 181
182 182
//go:embed templates/*.html static/*
215 215
import (
216 216
	"net/http"
217 217
218 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
219 -
	"github.com/stevedylandev/andromeda/crates-go/darkmatter"
220 -
	"github.com/stevedylandev/andromeda/crates-go/web"
218 +
	"github.com/stevedylandev/andromeda/pkg/auth"
219 +
	"github.com/stevedylandev/andromeda/pkg/darkmatter"
220 +
	"github.com/stevedylandev/andromeda/pkg/web"
221 221
)
222 222
223 223
func (a *App) routes() *http.ServeMux {
263 263
264 264
## db.go (or internal/store)
265 265
266 -
Single-file module: schema, model, CRUD. `crates-go/sqlite.Open` handles the
266 +
Single-file module: schema, model, CRUD. `pkg/sqlite.Open` handles the
267 267
driver, pragmas, and schema bootstrap.
268 268
269 269
```go
273 273
	"database/sql"
274 274
	"errors"
275 275
276 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
277 -
	"github.com/stevedylandev/andromeda/crates-go/sqlite"
276 +
	"github.com/stevedylandev/andromeda/pkg/auth"
277 +
	"github.com/stevedylandev/andromeda/pkg/sqlite"
278 278
)
279 279
280 280
type Item struct {
396 396
	"net/http"
397 397
	"strings"
398 398
399 -
	"github.com/stevedylandev/andromeda/crates-go/auth"
400 -
	"github.com/stevedylandev/andromeda/crates-go/web"
399 +
	"github.com/stevedylandev/andromeda/pkg/auth"
400 +
	"github.com/stevedylandev/andromeda/pkg/web"
401 401
)
402 402
403 403
func (a *App) loginGetHandler(w http.ResponseWriter, r *http.Request) {
467 467
import (
468 468
	"net/http"
469 469
470 -
	"github.com/stevedylandev/andromeda/crates-go/web"
470 +
	"github.com/stevedylandev/andromeda/pkg/web"
471 471
)
472 472
473 473
func (a *App) apiListItems(w http.ResponseWriter, r *http.Request) {
514 514
515 515
## Authentication
516 516
517 -
`crates-go/auth` provides three middleware patterns. Pick based on app shape:
517 +
`pkg/auth` provides three middleware patterns. Pick based on app shape:
518 518
519 519
### Session/cookie auth (web-facing apps)
520 520
631 631
632 632
## Dockerfile
633 633
634 -
Multi-stage, built from the repo root so `crates-go/` is available for the
634 +
Multi-stage, built from the repo root so `pkg/` is available for the
635 635
`replace` directives. Pure Go (`CGO_ENABLED=0`) because the SQLite driver is
636 636
`modernc.org/sqlite`.
637 637
639 639
# Build from repo root: docker build -t APP_NAME -f apps/APP_NAME/Dockerfile .
640 640
FROM golang:1.25-bookworm AS builder
641 641
WORKDIR /app
642 -
COPY crates-go/ ./crates-go/
642 +
COPY pkg/ ./pkg/
643 643
COPY apps/APP_NAME/go.mod apps/APP_NAME/go.sum ./apps/APP_NAME/
644 644
WORKDIR /app/apps/APP_NAME
645 645
RUN go mod download
763 763
make go-app-fmt  APP=APP_NAME
764 764
```
765 765
766 -
The shared crates (`crates-go/web`, `crates-go/auth`, `crates-go/config`,
767 -
`crates-go/sqlite`, `crates-go/darkmatter`) are each their own module — run
766 +
The shared crates (`pkg/web`, `pkg/auth`, `pkg/config`,
767 +
`pkg/sqlite`, `pkg/darkmatter`) are each their own module — run
768 768
`go build ./...` inside any to check in isolation.
769 769
770 770
## Checklist
776 776
3. `main.go` — env load, DB open, sessions bootstrap, `App` build, `ListenAndServe`.
777 777
4. `app.go` — `App` struct, `//go:embed`, page-data structs.
778 778
5. `routes.go` — mux wiring, middleware, `/static/` + `darkmatter.Mount`.
779 -
6. `db.go` — schema, model, CRUD via `crates-go/sqlite.Open`.
779 +
6. `db.go` — schema, model, CRUD via `pkg/sqlite.Open`.
780 780
7. `handlers_web.go` — login/logout + HTML CRUD pages.
781 781
8. `handlers_api.go` — JSON CRUD endpoints behind `RequireAPIKey`.
782 782
9. `templates/base.html` + per-page templates using `{{ define }}` blocks.
794 794
- No web frameworks (gin, echo, chi, fiber) — stdlib `net/http` mux is enough.
795 795
- No ORMs — raw `database/sql` + `?` placeholders.
796 796
- No connection pools — `sqlite.Open` sets `MaxOpenConns(1)` on purpose.
797 -
- No cgo SQLite drivers — use `modernc.org/sqlite` via `crates-go/sqlite`.
798 -
- No external CSS frameworks unless specified — use `crates-go/darkmatter`.
797 +
- No cgo SQLite drivers — use `modernc.org/sqlite` via `pkg/sqlite`.
798 +
- No external CSS frameworks unless specified — use `pkg/darkmatter`.
799 799
- No JS frontend / build step — `html/template` rendered server-side.
800 800
- No CLI / TUI deps (cobra, bubbletea) unless the app actually needs them.
801 -
- No `replace` directives for anything outside `crates-go/`.
801 +
- No `replace` directives for anything outside `pkg/`.