Skip to content

Repository files navigation

biatosh

A real-time presence "radar" web app — see who else is online and ping them with a click. Single Go binary, SQLite-backed, server-side rendered with templ, live-updated over Server-Sent Events.

                 ┌──────────────┐
                 │   browser    │
                 │ (HTMX + SSE) │
                 └───────┬──────┘
                         │ HTTPS / SSE
                         ▼
                 ┌──────────────┐
                 │   biatosh    │   single binary
                 │  (Fiber v3)  │   no CGO, ~16MB static
                 └───────┬──────┘
                         │ SQL
                         ▼
                 ┌──────────────┐
                 │   SQLite     │   modernc.org pure-Go driver
                 └──────────────┘

Quickstart

# Generate (templ + sqlc) and build a CGO-free binary
make generate
CGO_ENABLED=0 go build -ldflags="-w -s" -o biatosh .

# First run: migrate, seed system roles + 8 demo users, serve
./biatosh migrate up
./biatosh seed dev
./biatosh serve

# Visit http://127.0.0.1:8181 and log in as ali/ali (or any of:
# amin, reza, behzad, omid, sepi, yasin, masih — password = username).

For live-reload during development:

make dev       # runs `air`, which re-runs `templ generate` and `sqlc generate` before every build

Docker

make build           # docker build -t biatosh:latest .
make run-docker      # runs container on host port 8181, data volume at /data

The container automatically applies migrations and seeds system roles at startup. Demo users are NOT seeded in the container build — call biatosh seed dev explicitly if you want them.

Configuration

All settings come from environment variables (see internal/config/config.go).

Env var Default What it does
BIATOSH_HTTP_IP 127.0.0.1 bind address
BIATOSH_HTTP_PORT 8181 bind port
BIATOSH_DB_PATH db.sqlite SQLite file path
BIATOSH_SESSION_TTL 168h how long an issued session is valid
BIATOSH_SESSION_COOKIE biatosh_sid cookie name
BIATOSH_SESSION_SECURE true flip to false for plain-HTTP local dev
BIATOSH_SESSION_SAMESITE Lax cookie SameSite policy
BIATOSH_LOG_LEVEL info debug / info / warn / error

--ip, --port, --db-path CLI flags override the corresponding env vars.

CLI

biatosh serve                                  # HTTP server
biatosh migrate up|down|status                 # goose-managed schema
biatosh seed system                            # idempotent role + permission catalog
biatosh seed dev                               # system roles + 8 demo users
biatosh user create --username ... --password ... --email ... --name ...
biatosh user list
biatosh user grant <username> <role-slug>      # role-slug is "admin" or "user"
biatosh user revoke <username> <role-slug>

Architecture

Layered with consumer-side interfaces. The compile-time rule: anything in internal/transport/* may NOT import internal/repository/* directly — handlers must go through services.

cmd/                                  # cobra entrypoints, no business logic
main.go                               # signal-aware ctx → cmd.New().Execute()
internal/
  app/container.go                    # composition root; wires services
  config/                             # typed env-var Config
  contract/errors.go                  # sentinel errors crossed across services
  domain/
    user/                             # User type
    auth/                             # Session, Role, Permission, GroupRole
  migrations/                         # //go:embed *.sql; goose-managed
  observability/                      # *slog.Logger, /healthz, /readyz, /metrics
  platform/
    crypto/                           # argon2id wrapper
    ids/                              # UUIDv7 generator
  service/
    user/                             # Register, Authenticate, Get, List
    auth/                             # IssueSession, RBAC (Can, CanInGroup), SeedRolesAndPermissions
    audit/                            # Record(action, actor, target, ...)
    presence/                         # in-memory Hub for SSE roster + notify
  repository/sqlite/                  # adapters; sqlc-generated queries in database/
  transport/http/
    http.go                           # Fiber v3 app wiring
    router/                           # mounts /, /login, /events, /notify/:target
    middleware/                       # requestid, logger (slog), csrf, authn, authz, ctx
    controller/                       # LoginPage, LoginSubmit, Dashboard, Logout, Notify
    sse/                              # GET /events SSE handler
    render/                           # templ → fiber.Ctx adapter
  web/                                # templ component tree
    layout/, page/, component/, partial/

How the radar works

  1. Browser logs in → server issues a sessions row, sets opaque biatosh_sid cookie.
  2. Browser opens GET /events (Server-Sent Events). The handler registers a presence.Stream keyed by user id and broadcasts the new roster to everyone.
  3. Server-rendered HTML fragments (rendered by templ partials) are pushed as named SSE events:
    • event: radar → HTMX sse-swap="radar" replaces the #radar-users div
    • event: toast → HTMX appends a toast notification to #toasts
  4. Clicking a dot fires POST /notify/:targetID; the server finds that user's SSE stream and pushes a toast.
  5. Disconnect (browser close, tab close) cancels the stream context → hub deregisters → broadcasts again.

There's exactly one piece of custom JS (~10 lines, inline in radar.templ): a htmx:sseMessage listener that fires Notification API toasts as a desktop notification on top of the in-page toast.

Observability

  • /healthz — liveness (always 200 if the process can serve)
  • /readyz — readiness (200 only when the DB pings successfully)
  • /metrics — Prometheus text format: request counters by status class, goroutine count, allocated memory, uptime
  • structured slog JSON logs to stderr, level via BIATOSH_LOG_LEVEL
  • every request gets an X-Request-ID (UUIDv7); it's attached to access log lines

Modernization phases (completed)

The full plan lives at /home/farshad/.claude/plans/glistening-whistling-pond.md. All six phases have shipped:

  • ✅ Phase 1 — Foundation: internal/ skeleton, app.Container, typed config, signal-aware main.
  • ✅ Phase 2 — Persistence: goose migrations, pure-Go SQLite, real time.Time columns.
  • ✅ Phase 3 — Schema: UUIDv7 PKs, argon2id passwords, RBAC + sessions + groups + audit.
  • ✅ Phase 4 — Service layer: per-service consumer-side repositories, server-side revocable sessions, *slog.Logger, custom CSRF, authn/authz middleware.
  • ✅ Phase 5 — Fiber v3 + templ + SSE: HTMX-over-SSE replaces WebSocket; presence hub interface; no HTML files outside internal/web/.
  • ✅ Phase 6 — Observability + polish: /healthz//readyz//metrics, biatosh user list, README.

Releases

Packages

Used by

Contributors

Languages