2026-02-15 15:39:44 -06:00
2026-02-23 21:09:01 +00:00
2026-02-23 02:12:57 +00:00
2026-02-23 00:37:32 +00:00
2026-02-23 02:12:57 +00:00
2026-02-23 00:37:32 +00:00
2026-02-23 21:09:01 +00:00
2026-02-23 00:37:32 +00:00
2026-02-23 21:09:01 +00:00
2026-02-15 15:39:44 -06:00
2026-02-23 00:37:32 +00:00
2026-02-23 00:37:32 +00:00
2026-02-23 00:37:32 +00:00
2026-02-15 15:39:44 -06:00
2026-02-15 21:59:21 -06:00
2026-02-23 00:37:32 +00:00
2026-02-23 00:37:32 +00:00
2026-02-15 21:59:21 -06:00

subcult.tv

SUBCULT — Subculture Collective We build tools, media, and infrastructure for the counterculture.

Build License


Stack

Layer Choice Why
Frontend Vite + React 19 Fast builds, HMR, modern ESM-first bundling
Language TypeScript + Go Type safety on both sides
Styling Tailwind CSS v4 Utility-first with custom design tokens in CSS
Routing React Router v7 Client-side SPA routing with nested layouts
Content MDX (via @mdx-js/rollup) Markdown + JSX for zine posts with rich components
API Go + Chi v5 Lightweight, idiomatic HTTP router with middleware
Database PostgreSQL 16 Relational storage for projects, posts, contacts, subscribers
Auth JWT (HMAC SHA256) Stateless auth with bcrypt password hashing
Analytics Umami Self-hosted, privacy-first analytics
Data GitHub API + local JSON + DB Auto-populate projects from GitHub, override locally, manage via admin
Fonts Oswald / Libre Baskerville / JetBrains Mono Display / body / mono — punk + editorial + terminal
Infra Docker Compose PostgreSQL, Go API, and Umami in containers

Quick Start

# Clone
git clone https://github.com/subculture-collective/subcult-tv.git
cd subcult-tv

# Copy environment config
cp .env.example .env
# Edit .env with your values (DB password, JWT secret, admin password)

# Install frontend dependencies
npm install

# Start everything (Docker services + dev server)
make dev

# Or start services individually:
make docker-up       # Start PostgreSQL, API, Umami
make dev-frontend    # Start Vite dev server at http://localhost:5175
make dev-api         # Start Go API locally (outside Docker)

Without Docker (API only)

# Install Go dependencies
cd api && go mod download && cd ..

# Start PostgreSQL separately, then:
make dev-api         # Runs Go server on :8080

All Make targets

make help            # Show all available commands
make setup           # Install all dependencies (npm + Go)
make dev             # Docker services + Vite dev server
make build           # Build frontend + API for production
make lint            # ESLint
make typecheck       # TypeScript check
make check           # lint + typecheck
make format          # Prettier
make docker-up       # Start Docker services
make docker-down     # Stop Docker services
make docker-logs     # Tail Docker logs
make docker-rebuild  # Rebuild and restart containers
make db-shell        # Open psql shell
make db-reset        # Drop and recreate database (data loss!)
make clean           # Remove build artifacts

Environment Variables

# Database
POSTGRES_USER=subcult
POSTGRES_PASSWORD=your_secure_password
POSTGRES_DB=subcult_db
DATABASE_URL=postgres://subcult:your_secure_password@localhost:5432/subcult_db?sslmode=disable

# API
JWT_SECRET=your_jwt_secret_here_min_32_chars
ADMIN_USER=admin
ADMIN_PASSWORD=your_admin_password
CORS_ORIGINS=http://localhost:5173,https://subcult.tv
PORT=8080

# Umami Analytics
UMAMI_DATABASE_URL=postgresql://subcult:your_secure_password@postgres:5432/umami

# Frontend (optional)
VITE_GITHUB_TOKEN=ghp_your_token_here      # GitHub API (higher rate limits)
VITE_API_URL=                                # Override API base URL (defaults to /api via proxy)

Routes

Public Pages

Route Page Description
/ Home Hero, featured projects, manifesto, latest posts, newsletter signup, Patreon CTA
/projects Projects Filterable grid of all projects (type + status filters)
/projects/:slug Project Detail Full project page with cover art, description, stack, links, CTA
/support Patreon Tier blocks, what support funds, shipping cadence, CTA
/about About / Studio Mission, values, what we build/won't build, toolchain
/zine Zine Index of all MDX posts
/zine/:slug Post Individual MDX post with full content
/contact Contact API-backed contact form with mailto fallback, social links
/press Press Kit Brand identity, color palette, typography, usage rules

Admin Pages

Route Page Description
/admin/login Login Admin authentication
/admin Dashboard Stats overview (projects, posts, contacts, subscribers)
/admin/projects Projects CRUD Create, edit, delete projects
/admin/posts Posts CRUD Create, edit, delete posts (with publish toggle)
/admin/contacts Contacts View contact submissions, mark read, delete
/admin/subscribers Subscribers View newsletter subscribers

API Endpoints

All API routes are prefixed with /api/v1.

Public

Method Endpoint Description
GET /api/health Health check
GET /api/v1/projects List projects (?status=, ?type=)
GET /api/v1/projects/:slug Get project by slug
GET /api/v1/posts List published posts (paginated)
GET /api/v1/posts/:slug Get post by slug
POST /api/v1/contacts Submit contact form
POST /api/v1/newsletter/subscribe Subscribe to newsletter
GET /api/v1/newsletter/confirm/:token Confirm subscription
DELETE /api/v1/newsletter/unsubscribe Unsubscribe

Protected (requires Authorization: Bearer <token>)

Method Endpoint Description
POST /api/v1/auth/login Login → returns JWT
GET /api/v1/auth/me Current user info
POST /api/v1/projects Create project
PUT /api/v1/projects/:id Update project
DELETE /api/v1/projects/:id Delete project
POST /api/v1/posts Create post
PUT /api/v1/posts/:id Update post
DELETE /api/v1/posts/:id Delete post
GET /api/v1/contacts List contacts (paginated)
PATCH /api/v1/contacts/:id/read Toggle read status
DELETE /api/v1/contacts/:id Delete contact
GET /api/v1/newsletter/subscribers List subscribers (paginated)
GET /api/v1/admin/stats Dashboard statistics

Project Structure

subcult_tv/
├── api/                          # Go backend
│   ├── cmd/server/main.go        # Entry point, graceful shutdown, admin seeding
│   ├── internal/
│   │   ├── config/               # Environment-based config
│   │   ├── database/             # PostgreSQL connection + migrations
│   │   │   └── migrations/       # Embedded SQL migrations
│   │   ├── handlers/             # HTTP handlers (auth, projects, posts, contacts, newsletter, admin)
│   │   ├── middleware/           # JWT auth + request logger
│   │   ├── models/               # Domain types
│   │   └── router/               # Chi router setup with CORS
│   ├── Dockerfile                # Multi-stage Alpine build
│   ├── go.mod
│   └── go.sum
├── content/
│   ├── posts/                    # MDX blog posts
│   │   ├── subcult-manifesto.mdx
│   │   ├── release-log-field-notes.mdx
│   │   └── how-we-build.mdx
│   └── projects.json             # Local project overrides
├── docker/
│   └── postgres/init.sql         # Creates umami database
├── public/
│   ├── favicon.svg
│   ├── og-image.svg
│   ├── robots.txt
│   ├── sitemap.xml
│   └── press-kit/
│       └── README.md
├── src/
│   ├── components/
│   │   ├── admin/                # AdminLayout
│   │   ├── effects/              # GlitchFrame, TerminalPanel, CoverArt
│   │   ├── ui/                   # Button, Card, Tag
│   │   ├── NewsletterSignup.tsx
│   │   └── ...
│   ├── context/                  # EffectsContext, AuthContext
│   ├── lib/
│   │   ├── api.ts                # Typed API client for Go backend
│   │   ├── github.ts             # GitHub API fetch layer
│   │   └── posts.ts              # Post registry
│   ├── pages/
│   │   ├── admin/                # Admin pages (Login, Dashboard, Projects, Posts, Contacts, Subscribers)
│   │   └── ...                   # Public pages
│   ├── App.tsx                   # Route definitions (public + admin)
│   ├── main.tsx                  # Entry point (AuthProvider + EffectsProvider)
│   ├── types.ts                  # Shared types
│   └── index.css                 # Full design system + effects
├── docker-compose.yml            # PostgreSQL, API, Umami services
├── Makefile                      # Development commands
├── DESIGN_NOTES.md
├── CONTENT_CHECKLIST.md
├── .env.example
├── index.html
├── package.json
└── vite.config.ts

Design System

Colors

Token Hex Usage
void #0a0a0a Primary background
signal #ff3333 Primary accent (CTAs, links)
static #00ff88 Terminal green, success
flicker #ffcc00 Warning, highlights
scan #6633ff Secondary accent
chalk #e8e0d0 Primary text
bone #c4b9a7 Body text

Effects

Three configurable levels via the FX toggle in the nav:

  • Clean — No visual effects. Maximum readability.
  • Mild Glitch — Subtle scanlines, light noise. Default.
  • Full VHS — Scanlines, noise, VHS tracking lines, chromatic aberration.

All effects respect prefers-reduced-motion and are CSS-first (no GPU shaders).

Deployment

Deploy the Go API + PostgreSQL + Umami on a VPS, and the frontend on Vercel/Cloudflare Pages with VITE_API_URL pointing to your API domain.

# On your VPS:
cp .env.example .env
# Edit .env with production values
docker compose up -d

# Umami will be available at http://your-server:3001
# API at http://your-server:8080

Frontend Only (Vercel)

npm i -g vercel && vercel --prod

vercel.json is included for SPA fallback rewrites.

Set environment variables in Vercel dashboard:

  • VITE_API_URL — Your API server URL (e.g., https://api.subcult.tv)
  • VITE_GITHUB_TOKEN — Optional, for higher GitHub API rate limits

Frontend Only (Cloudflare Pages)

  1. Connect GitHub repo → Build command: npm run build → Output: dist
  2. Add environment variables in the dashboard

Umami Analytics Setup

  1. After docker compose up, visit http://localhost:3001
  2. Default login: admin / umami
  3. Add your website, copy the Website ID
  4. Update index.html with the Website ID in data-website-id
  5. For production, update the Umami script src to your Umami domain

DNS

Point subcult.tv to your deployment provider. Point api.subcult.tv to your API server.

Admin Access

After starting the services, the admin user is automatically seeded from .env:

Username: $ADMIN_USER (default: admin)
Password: $ADMIN_PASSWORD

Navigate to /admin/login to access the admin dashboard.

License

MIT — do what you want, credit appreciated.

Description
No description provided
Readme MIT 2.7 MiB
Languages
TypeScript 68.1%
Go 16.8%
MDX 6.3%
CSS 4.9%
JavaScript 1.8%
Other 2.1%