Project
DevinMarshall.info
DevinMarshall.info is the digital home of an Imagineer — a personal brand platform designed to bring my ideas, work, story, and mission into one intentional ecosystem. Built with Next.js, TypeScript, Tailwind, and Supabase, the site functions as more than a portfolio; it is a living foundation for content, community, commerce, and long-term brand growth. This project reflects my commitment to building with purpose, designing with clarity, and creating an online presence that evolves with the life I am intentionally shaping.
DevinMarshall.info was built to unify brand storytelling, content operations, and productized business workflows in a single platform instead of a fragmented stack of tools. The implementation uses Next.js 16 App Router, React 19, TypeScript, Tailwind CSS 4, and Supabase PostgreSQL/Auth, with integrated Stripe checkout, Printful fulfillment, newsletter and campaign tooling, and IndieWeb-compatible publishing endpoints (IndieAuth, Micropub, Webmention) plus outward syndication connectors. The technical scope includes a large admin surface for managing content, media, portfolio entries, community features, and marketing analytics, alongside an extensive automated test suite that validates auth, APIs, commerce flows, and content integrity; current support focuses on production operations with ongoing expansion of campaign intelligence, publishing workflows, and platform integrations.
DevinMarshall.info is more than a website — it is the central hub of my personal brand and the digital home of an Imagineer. I built this platform to house the full scope of my work: my philosophy, creative output, professional portfolio, community initiatives, and the evolving vision behind Project Lifescape. Rather than scattering my identity across disconnected platforms, I wanted one intentional space that reflects who I am and what I am building.
What makes this project especially meaningful to me is that it embodies the values I stand for: intentionality, craftsmanship, clarity, and disciplined creativity. This site is proof of concept for the way I approach life and work — not by default, but by design. It is both a portfolio piece and a living expression of my long-term vision as a builder, strategist, storyteller, and creator.
Project README
devinmarshall.info
Personal brand website for Devin Marshall — "Live by design, NOT by default."
Built with Next.js 16, React 19, TypeScript, and Tailwind CSS 4.
Includes IndieWeb publishing support with IndieAuth, Micropub, Webmention discovery, and lightweight note/reply/bookmark permalinks.
Tech Stack
| Layer | Technology | Version |
|---|---|---|
| Framework | Next.js (App Router, Turbopack) | 16.2.2 |
| UI Library | React | 19.2.3 |
| Language | TypeScript (strict) | 5 |
| Styling | Tailwind CSS (CSS custom properties) | 4 |
| Database | Supabase (PostgreSQL) | latest |
| Payments | Stripe (Checkout, Cash App Pay) | 20.4.1 |
| Fulfillment | Printful (REST API) | — |
| Google Workspace (Gmail API) | — | |
| Validation | Zod | 4 |
| Testing | Vitest | 4 |
| Icons | Lucide React, React Icons | latest |
| Fonts | Geist Sans & Geist Mono | built-in |
Getting Started
# Install dependencies
npm install
# Copy environment file and configure
cp .env.example .env.local
# Add Supabase credentials (from Supabase Dashboard → Settings → API)
# NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
# NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
# SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Create an admin user in Supabase Dashboard → Authentication → Users
# Run database migration (paste in Supabase SQL Editor)
# See supabase/migration.sql
# Seed database from JSON data (first-time setup only)
node scripts/seed-supabase.mjs
# Start development server
npm run dev
Open localhost or devinmarshall.info to view the site.
Environment Variables
See .env.example for required variables:
| Variable | Description |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Supabase project URL (from Dashboard → Settings → API) |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase anonymous/public key (from Dashboard → Settings → API) |
SUPABASE_SERVICE_ROLE_KEY |
Supabase service role key (server-side only, bypasses RLS) |
STRIPE_SECRET_KEY |
Stripe secret key (commerce — server-side only) |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
Stripe publishable key (commerce — client-side) |
STRIPE_WEBHOOK_SECRET |
Stripe webhook signing secret |
PRINTFUL_API_KEY |
Printful API token (commerce — server-side only) |
PRINTFUL_STORE_ID |
Printful store ID |
GMAIL_CLIENT_ID |
Google OAuth2 client ID (inbox — server-side only) |
GMAIL_CLIENT_SECRET |
Google OAuth2 client secret (inbox — server-side only) |
GMAIL_REFRESH_TOKEN |
Google OAuth2 refresh token (inbox — server-side only) |
GMAIL_USER_EMAIL |
Gmail user email address for @devinmarshall.info |
PUBLISH_API_KEY |
API key for external publishing via Obsidian plugin |
BLUESKY_HANDLE |
Bluesky handle (e.g. devinmarshall.bsky.social) |
BLUESKY_APP_PASSWORD |
Bluesky app password (AT Proto auth) |
MASTODON_INSTANCE_URL |
Mastodon instance base URL (e.g. https://mastodon.social) |
MASTODON_ACCESS_TOKEN |
Mastodon OAuth2 access token |
MEDIUM_INTEGRATION_TOKEN |
Medium integration token |
MEDIUM_AUTHOR_ID |
Medium author ID |
TWITTER_API_KEY |
Twitter/X OAuth 1.0a consumer key |
TWITTER_API_SECRET |
Twitter/X OAuth 1.0a consumer secret |
TWITTER_ACCESS_TOKEN |
Twitter/X OAuth 1.0a access token |
TWITTER_ACCESS_TOKEN_SECRET |
Twitter/X OAuth 1.0a access token secret |
LINKEDIN_ACCESS_TOKEN |
LinkedIn OAuth2 access token |
LINKEDIN_PERSON_URN |
LinkedIn person URN (urn:li:person:...) |
REDDIT_CLIENT_ID |
Reddit app client ID |
REDDIT_CLIENT_SECRET |
Reddit app client secret |
REDDIT_USERNAME |
Reddit account username |
REDDIT_PASSWORD |
Reddit account password |
REDDIT_DEFAULT_SUBREDDIT |
Default subreddit for Reddit posts (optional) |
THREADS_USER_ID |
Threads user ID |
THREADS_ACCESS_TOKEN |
Threads Graph API access token |
FACEBOOK_PAGE_ID |
Facebook Page ID |
FACEBOOK_PAGE_ACCESS_TOKEN |
Facebook Page access token |
SPOTIFY_CLIENT_ID |
Spotify app client ID (Now Playing widget) |
SPOTIFY_CLIENT_SECRET |
Spotify app client secret (Now Playing widget) |
SPOTIFY_REFRESH_TOKEN |
Spotify OAuth2 refresh token — run node scripts/get-spotify-token.mjs to generate |
AWS_SNS_ACCESS_KEY_ID |
AWS IAM access key ID (SMS notifications via SNS) |
AWS_SNS_SECRET_ACCESS_KEY |
AWS IAM secret access key (SMS notifications via SNS) |
AWS_SNS_REGION |
AWS region for SNS (e.g. us-east-1) |
Project Structure
src/
├── app/ # Next.js App Router pages
│ ├── layout.tsx # Root layout (ThemeProvider, Header, Footer)
│ ├── page.tsx # Landing page (/)
│ ├── globals.css # Design tokens & base styles
│ ├── home/ # /home
│ ├── about/ # /about, /philosophy, /imagineer-framework, /project-lifescape
│ ├── portfolio/ # /portfolio plus item detail pages for projects, creative works, and case studies
│ ├── content/ # /blog, /vlogs, /gallery
│ ├── resources/ # /resources, /reading-list, /tools, /downloads
│ ├── login/ # /login — unified login (email+password; role-based redirect; ?next= support)
│ ├── signup/ # /signup — unified signup (email confirmation; password requirements)
│ ├── recover/ # /recover — password recovery (?mode=reset shows reset form)
│ ├── account/ # /account — unified account (auth required; subscription + security)
│ ├── community/ # /community — layout + Book Club (/community/book-club/*), Events (planned)
│ ├── book-club/ # old /book-club/* — files still present; all URLs 301-redirect to /community/book-club/* via next.config.ts
│ ├── resume/ # /resume
│ ├── connect/ # /connect
│ ├── brand-identity/ # /brand-identity (public brand guidelines & visual language)
│ ├── go/ # /go/[slug] — tracked short link redirects (302 + click recording)
│ ├── admin/ # Admin dashboard (authenticated, 6-category sidebar, no footer)
│ │ ├── marketing/ # Marketing Intelligence Dashboard
│ │ ├── campaigns/ # Campaign CRUD + /campaigns/new (Launch Wizard)
│ │ ├── landing-pages/ # Landing page editor + templates
│ │ └── portfolio/ # Portfolio CRUD + relationship editor with duplicate and case-study-from-project shortcuts
│ ├── api/ # API routes (auth, images, social-links, upload, contact, gallery, checkout, webhooks, newsletter, admin CRUD, book-club, campaigns, marketing, portfolio relationships)
│ │ ├── campaigns/track/ # Public event tracking (page_view, cta_click, purchase) + email pixel/click
│ │ └── admin/marketing/ # Marketing dashboard data, comparison, attribution
│ └── ... # booking, community, fund, now, media, shop, support, search, legal
├── components/
│ ├── layout/ # Header (with UserMenu), Footer (hidden on admin via body.admin-page CSS)
│ ├── ui/ # ContactForm, NewsletterForm, ThemeToggle, ThemeProvider, ThemeLogo, HeroSection
│ ├── shop/ # BuyButton (client-side Stripe checkout)
│ ├── admin/ # AdminStub, RevisionHistory, SyndicationPublisher (unified POSSE compose/publish UI)
│ │ # SyndicationTextGenerator, SyndicationPanel (deprecated — superseded by SyndicationPublisher)
│ │ # NotificationBell (bell icon + unread badge + dropdown; Supabase Realtime-powered)
│ └── ui/ # BannerZone (server component — affiliate ad renderer)
├── lib/
│ ├── data/ # Supabase data access layer (social-links, images, contact, uploads, products, orders, revisions, inbox, banners, events, resources, community, funding, content, testimonials, now, order-analytics, syndications, webmentions, notifications, campaigns, campaign-updates, landing-pages, short-links, featured, search-telemetry, search-index, resume, donations, donation-methods, impact-areas, brand-config, site-settings, cart, page-content, book-club, newsletters)
│ ├── syndication-connectors/ # POSSE platform connectors — lazy-loaded per dispatch
│ │ ├── types.ts # Shared interfaces: PlatformConnector, SyndicationPayload, SyndicationResult, ConnectorOptions
│ │ ├── registry.ts # Capability map, isPlatformConfigured(), loadConnector() dynamic import
│ │ ├── media.ts # fetchImageAsBuffer(), selectImageUrl(), PLATFORM_IMAGE_LIMITS
│ │ ├── bridgy.ts # pingBridgy(), pingBridgyForResults() — IndieWeb backfeed via brid.gy
│ │ ├── bluesky.ts # AT Proto (com.atproto + app.bsky.feed.post), blob upload
│ │ ├── mastodon.ts # Mastodon v2/media + v1/statuses
│ │ ├── medium.ts # Medium API v1 with canonical URL footer
│ │ ├── twitter.ts # OAuth 1.0a built on Node crypto, v2/tweets
│ │ ├── linkedin.ts # LinkedIn REST API (202401), x-restli-id header
│ │ ├── reddit.ts # Password grant auth + oauth/api/submit
│ │ ├── threads.ts # Two-step container + publish flow (5s delay)
│ │ └── facebook.ts # Graph API v21.0 feed post
│ ├── types/ # TypeScript type definitions (images.ts, notification.ts)
│ ├── supabase.ts # Supabase client (lazy-initialized, server-side only)
│ ├── constants.ts # Brand pillars, nav config, social link defaults, CAMPAIGN_TRANSITIONS, DEFAULT_CHANNELS_BY_CATEGORY, CAMPAIGN_STATUSES
│ ├── notification-constants.ts # Notification event types, categories, channels (single source of truth)
│ ├── platform-icons.tsx # 34 platform brand icon mappings
│ ├── auth.ts # Supabase Auth session helpers
│ ├── env.ts # Central environment validation (fail-fast, commerce + SYNDICATION_ENV + SNS_ENV)
│ ├── rate-limit.ts # Supabase-backed persistent rate limiter
│ ├── commerce.ts # Shared commerce helpers (normalization, mapping)
│ ├── stripe.ts # Stripe server helpers (Checkout, webhooks)
│ ├── printful.ts # Printful API client (fulfillment)
│ ├── gmail.ts # Gmail API client (OAuth2, fetch-based)
│ ├── gmail-sync.ts # Gmail sync engine (full + incremental)
│ ├── audit-log.ts # Audit log helper
│ ├── notifications.ts # Notification dispatcher (fire-and-forget, dedup, email + SMS channels)
│ ├── sns.ts # AWS SNS client (lazy-initialized, E.164 validation, SMS transactional)
│ ├── newsletter-helpers.ts # Newsletter campaign helpers (send, segment, instrumentEmailHtml for tracking pixel/click injection)
│ ├── campaign-tracking.ts # Campaign event tracking helpers, visitor cookie management
│ ├── syndication-helpers.ts # POSSE text generators for 13 platforms (Twitter/X, LinkedIn, Bluesky, Mastodon, Threads, Reddit, + 7 more)
│ ├── markdown.ts # Markdown → HTML rendering; `renderMarkdown()` (block, GFM+breaks) + `renderInlineMarkdown()` (inline, no block wrappers, XSS-sanitized)
├── middleware.ts # Auth guard (admin + /community/book-club/library|account), CSRF, rate limiting, CSP headers; unauthenticated redirect to /login?next=
supabase/
├── migration.sql # Database schema (10 tables, indexes, RLS policies)
├── phase4.sql # Content entries schema
├── phase5.sql # Commerce schema (10 commerce tables)
├── phase6.sql # Inbox schema (4 tables)
├── phase7.sql # Banners schema (affiliate ads)
├── phase8.sql # RLS policies for banners/banner_events
├── phase9.sql # Newsletter campaigns schema
├── phase10-notifications.sql # Notifications table + Realtime publication + site_settings seed (43 preference rows)
├── phase11-landing-positions.sql # Normalize JSONB field names in landing_sections + seed image position defaults
├── migrations/ # Incremental migrations (hero media, now page, resume, donations, syndication+webmentions,
│ # book club, landing pages, hero style, page content, campaigns,
│ # syndication_enhancements, section injection, short links, visitor tracking,
│ # campaign emails, campaign channels, campaign creatives, LP templates)
scripts/
├── seed-supabase.mjs # One-time data migration from JSON to Supabase
├── seed-founding-supporters.mjs # Seed founding supporters data
├── seed-page-content.mjs # Seed per-page content slots
├── seed-resume.mjs # Seed resume/profile data
tests/
├── env.test.ts # Environment validation tests
├── password-validation.test.ts # Password strength rule tests
├── upload-validation.test.ts # SVG safety & filename validation tests
├── contact-validation.test.ts # Contact form validation tests
├── middleware-auth.test.ts # RBAC & route classification tests
├── recovery-validation.test.ts # Password recovery tests
├── commerce-foundation.test.ts # Commerce env, Stripe params, Printful helpers
├── commerce-runtime.test.ts # Checkout normalization, order mapping
├── webhook-checkout.test.ts # Checkout normalization, shipping, fulfillment
├── validation.test.ts # Shared validation helpers (parseBody, Zod schemas)
├── banner-validation.test.ts # Banner URL, zone, size, scheduling validation
├── inbox-sync.test.ts # Gmail sync engine tests
├── reading-time.test.ts # Blog reading time estimation
├── order-analytics.test.ts # Order analytics snapshot & trend queries
├── newsletter-subscribe.test.ts # Newsletter subscribe endpoint (rate limit, idempotent)
├── admin-analytics-api.test.ts # Analytics API (site health, banner, commerce)
├── admin-content-api.test.ts # Content admin CRUD + auth guards
├── admin-events-api.test.ts # Events admin CRUD + auth guards
├── admin-resources-api.test.ts # Resources admin CRUD + auth guards
├── admin-community-api.test.ts # Community admin CRUD + duplicate handling
├── admin-funding-api.test.ts # Funding campaigns & tiers CRUD + auth guards
├── admin-products-api.test.ts # Products admin CRUD + auth guards
├── newsletter-helpers.test.ts # Newsletter campaign helper tests
├── admin-syndications-api.test.ts # Syndications admin CRUD + auth guards
├── webmention-endpoint.test.ts # Public /api/webmention endpoint (W3C spec, Bridgy detection)
├── syndication-connectors.test.ts # POSSE connector unit tests (Bluesky, Mastodon, Medium — dry-run, success, failure)
├── syndication-publish.test.ts # /api/admin/syndications/publish endpoint tests
├── bridgy-backfeed.test.ts # Bridgy backfeed + pingBridgy / pingBridgyForResults unit tests
├── cart-context.test.ts # Cart context helpers
├── checkout-custom-mode.test.ts # Checkout custom mode tests
├── markdown-sanitization.test.ts # Markdown XSS sanitization tests
├── page-content-slots.test.ts # Page content slot tests
├── portal-api.test.ts # Shop portal API tests
├── portal-onboarding.test.ts # Shop portal onboarding tests
├── admin-newsletters-api.test.ts # Newsletters admin CRUD + send + auth guards
├── notifications.test.ts # Notification dispatcher (channels, dedup, error isolation, auditLog)
├── notifications-api.test.ts # Notifications + notification-preferences API route tests
├── marketing-command-center.test.ts # Marketing Command Center (84 tests — state machine, wizard, health score rubric, attribution, email tracking)
├── campaign-analytics.test.ts # Campaign analytics & metrics
├── campaign-marketing.test.ts # Campaign marketing helpers
├── featured-items.test.ts # Featured item spotlight tests
├── search-telemetry.test.ts # Search telemetry tracking tests
└── gallery-image-metadata.test.ts # Gallery alt text editing rules, isFilenameAlt, updateImageMetaInAlbum (14 tests)
├── BRAND_IDENTITY.md # Full brand identity & design system
├── SITE_MAP.md # Website information architecture
├── Unfinished_Implementations.md # Known gaps and partial implementations
├── OBSIDIAN_PLUGIN.md # Obsidian publishing plugin setup
└── README.md # This file
Key Features
- Full-bleed hero sections — viewport-height hero images and videos on all 16 pages, hidden by default until media is uploaded. Admin-configurable fit, position, and text overlay controls. Supports video heroes (MP4, WebM, OGG) via reusable
HeroSectioncomponent. - Dark mode default with light mode toggle (persisted via localStorage, anti-FOUC inline script prevents theme flash)
- Admin dashboard with Supabase Auth (cookie-based sessions via
@supabase/ssr) for managing hero images, card images, gallery albums, social links, and hero content - Image management — upload images and videos (up to 100MB) via signed URL direct-to-Supabase-Storage flow, fit/position controls, profile images, gallery albums — all from the admin panel. Gallery images support click-to-edit alt text + caption via a modal overlay, missing-alt indicators (amber dot per thumbnail, count badge per album), and hover/tap gradient overlays on public gallery surfaces (portfolio detail pages + gallery page album modal).
- 55+ social platform links with conditional visibility and brand icons
- SEO optimized — OpenGraph/Twitter cards, JSON-LD structured data, dynamic sitemap, robots.txt
- Accessible — skip-to-content, focus-visible styles, ARIA attributes, keyboard navigation
- Security hardened — CSP headers, CSRF validation, Content-Type enforcement, HSTS, Supabase-backed audit logging, SVG upload sanitization
- RBAC enforced — admin role via Supabase
user_metadata.role, checked in middleware andisAdmin()helper - Password strength — minimum 12 characters with uppercase, lowercase, digit, and special character requirements
- Account recovery — email-based password recovery for locked-out admins via Supabase Auth
- Fully responsive — mobile-first layouts with
100dvhdynamic viewport units, tested at 375px, 768px, 1280px+ - Supabase-backed — all data stored in PostgreSQL via Supabase with Row Level Security, persistent rate limiting and audit logging. List queries support pagination.
- Commerce system — Stripe + Cash App Pay checkout, Printful print-on-demand fulfillment, admin product & variant management, order tracking, webhook-driven state machine for payments and fulfillment
- Gmail inbox — Google Workspace integration for
@devinmarshall.infoemail; admin inbox with read, reply, archive, star, trash; incremental sync via Gmail History API; on-demand attachment downloads - Affiliate banners — admin-managed banner ads with 7 IAB sizes, 6 placement zones (hero-below, mid-content, pre-footer, sidebar, in-grid, header-bar), page targeting, date-based scheduling, priority resolution; zero-footprint
BannerZoneserver component renders nothing when inactive. Impression/click tracking viabanner_eventstable, tag-based cache invalidation, weighted rotation for same-priority banners, device-aware rendering, and admin analytics dashboard. - Newsletter — functional subscribe/unsubscribe flow; public POST endpoint with rate limiting (3 per 15 min per IP), email validation, idempotent for existing subscribers; unsubscribe via GET endpoint returning styled HTML page;
NewsletterFormcomponent with loading/success/error states - Admin dashboard — dashboard home page displays site health metrics, banner performance (impressions, clicks, CTR, top banners), commerce metrics (orders, revenue, AOV, status breakdowns, top products, daily trends), brand performance, security status, and recent audit activity. Sidebar navigation organized into six categories (Marketing, Content, Brand, Commerce, Community, System). Site footer hidden on admin pages.
- Marketing Command Center — unified marketing system from idea to analytics. Unified campaign editor at
/admin/campaigns/[id]with tab registry (11 tabs filtered bycampaign_type). Marketing campaigns: settings, channels, banners, emails, creatives, analytics, revisions. Funding campaigns: settings, channels, banners, tiers, milestones, updates, supporters, analytics, revisions. Launch Wizard with Step 0 type-select (marketing: 5 steps; funding: adds goal/dates/initial-tier step). Lifecycle state machine (draft → scheduled → active → paused → completed). Marketing Intelligence Dashboard at/admin/marketingwith health scores (0-100, setup-completeness rubric withfailingChecksbreakdown), funnel visualization, campaign comparison, channel performance, email analytics, and multi-touch attribution (first-touch, last-touch, linear models). Email tracking via pixel + click redirect withinstrumentEmailHtml(). Visitor identity stitching via first-partyvisitor_idcookie. Campaign ↔ newsletter bidirectional linking. Clone campaigns with deep copy. Default channel templates per category. Phase 1 schema migration unified marketing and funding campaigns viacampaign_type_enumandfunding_campaigns.marketing_campaign_idFK. - Page Content CMS — 15-section-type CMS powering all static pages and enabling true new-page creation from the admin. Section types:
heading,rich_text,card_grid,cta,quote,image,image_gallery,video,divider,accordion,code_block,table,embed,button_group,spotify_now_playing. Pages created through the CMS publish as first-class public routes at/{slug}via a catch-all dynamic route. Reserved slugs (all existing route folders) are blocked at the API and admin UI. New pages start from one of 4 starter templates (Standard, Services, Landing, Announcement) that auto-seed sections on creation. Every text field is markdown-native: multi-line prose fields use the MarkdownToolbar +renderMarkdown()(block), and short fields (titles, captions, labels, table cells) userenderInlineMarkdown()(inline, no<p>wrapper). Admin hint text appears beneath every inline-capable input. Sections have an editablesectionKey, custom injection point + mode, and a Change Type control that resets content to empty. Default keys likeintrocan now replace page H1/subtitle/description content directly, and the Aboutbiokey uses a dedicated structured editor with headline, summary, role tags, and a public-style preview. - Spotify Now Playing —
spotify_now_playingCMS section type renders a live widget showing what Devin is listening to via the Spotify Web API. Falls back to most-recently played, and now shows a usable playback action even when Spotify preview clips are unavailable. Card or compact style. RequiresSPOTIFY_CLIENT_ID,SPOTIFY_CLIENT_SECRET,SPOTIFY_REFRESH_TOKEN. Token generated vianode scripts/get-spotify-token.mjs. - Now Page — markdown-native
/nowpage showing what Devin is currently focused on. Editable from admin dashboard at/admin/nowwith draft/published visibility toggle and live markdown preview. Inspired by the nownownow.com movement. - Obsidian publishing — Obsidian plugin publishes notes directly to the blog via
/api/publish(API key auth, upsert-by-slug). Supports multiple sites — add any website running the publish endpoint. - Public brand identity page —
/brand-identitydisplays brand pillars, narrative, voice & tone, color palette (with copy-hex buttons), typography, visual assets, and moodboard grid — all driven by the admin brand config - POSSE / IndieWeb — Full POSSE stack (Publish on your Own Site, Syndicate Elsewhere). One-click parallel dispatch to up to 8 live platforms (Twitter/X, LinkedIn, Bluesky, Mastodon, Threads, Reddit, Medium, Facebook) via the
SyndicationPublishercomponent andPOST /api/admin/syndications/publish. Each connector lives insrc/lib/syndication-connectors/. Compose step: per-platform custom text with char counters, image attach, article mode, dry-run. Platform health dashboard on/admin/syndications. Bridgy backfeed auto-pings after publishing to Twitter/Mastodon/Bluesky so likes, reposts, and replies flow back as webmentions.content_syndicationsstoreserror_message,platform_post_id, andmedia_urlper attempt. Blog posts display syndication links withu-syndicationmicroformat class. W3C-compliant webmention receiving endpoint (/api/webmention) with rate limiting, form-encoded + JSON support, and 202 Accepted responses. Verified webmentions show like/reply/repost counts on blog posts.<link rel="webmention">discovery header on all blog posts. Admin manages incoming webmentions at/admin/webmentionswith per-type stats, bulk-verify, and individual approve/reject. - Book Club — Subscription-gated digital library with Stripe billing. Members get unlimited access to PDF/EPUB books stored in a private Supabase bucket. Features: 127+ genre taxonomy with hierarchy, curated collections, per-user reading progress, star ratings & reviews, discussion prompts, book club events, 5% free preview enforcement, admin CRUD at
/admin/book-club, and admin notification emails to all active subscribers via Gmail. Public routes live at/community/book-club/**, with the older/book-club/**URLs permanently redirected. - Admin contact inbox — Admin
/admin/inboxincludes a "Contact Form" tab alongside Gmail, showing all contact form submissions with full detail view. Delete functionality hard-removes submissions with audit log. - Admin notification system — Real-time admin dashboard notifications for 10 event types (new orders, contact submissions, new subscribers, Stripe errors, fulfillment updates, webmentions, community activity, donations, content published, system errors). Bell icon in top bar with unread badge and dropdown preview; dedicated
/admin/notificationspage with date grouping, filters, bulk delete, and cleanup. Three delivery channels: dashboard (Supabase), email (Gmail), and SMS (AWS SNS). Per-event channel toggles in Settings → Notifications. Fire-and-forget dispatcher with SHA-256 deduplication (5-minute window),Promise.allSettledper-channel isolation, andauditLogon failures. Realtime badge updates via Supabase Realtime. - Tested — 847 tests via Vitest across 76 files covering env validation, password strength, upload/SVG safety, contact validation, recovery, RBAC middleware, commerce logic, inbox sync, banner validation, newsletter helpers, newsletter admin, order analytics, webmention endpoint, syndications admin, syndication connectors, syndication publish endpoint, Bridgy backfeed, marketing command center (84 tests: state machine, wizard, health score rubric with HealthScoreInput/HealthScoreResult, attribution, email tracking), campaign analytics, featured items, search telemetry, markdown sanitization (block + inline XSS), page content slots, page section rendering parity, intro-heading editing, the About bio editor, CMS page creation (reserved slugs, starter templates), all admin CRUD routes (content, events, resources, community, funding, products, newsletters), and gallery image metadata (isFilenameAlt, alt length validation, updateImageMetaInAlbum state transform)
- Vercel Analytics — privacy-friendly analytics via
@vercel/analytics - Performance optimized — public data getters cached via
unstable_cache(300s TTL, tag-based invalidation on admin writes), responsivesizeson all fill images, anti-FOUC theme detection
Scripts
npm run dev # Start dev server (Turbopack)
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLint
npm test # Run Vitest test suite
npm run test:watch # Run tests in watch mode
# Database
node scripts/seed-supabase.mjs # Seed Supabase from JSON files (first-time only)
node scripts/seed-page-content.mjs # Seed per-page content slots
node scripts/seed-resume.mjs # Seed resume/profile data
node scripts/seed-founding-supporters.mjs # Seed founding supporters
Brand
| Element | Value |
|---|---|
| Primary Color | Flame Red #D33D3C |
| Secondary Color | Steel Gray #AFAEAE |
| Dark Background | #1A1A1A |
| Tagline | "Live by design, NOT by default." |
| Motto | "Imagine. Design. Build." |
| Brand Pillars | The Imagineer · The Builder · The Analyst · The Storyteller · The Creator · The Veteran |
Full brand identity documented in BRAND_IDENTITY.md.
Documentation
| Document | Description |
|---|---|
| BRAND_IDENTITY.md | Brand identity, color theory, typography, voice & tone, pillars |
| SITE_MAP.md | Complete website information architecture & page descriptions |
| Unfinished_Implementations.md | Known gaps, partial implementations, and deferred work |
| OBSIDIAN_PLUGIN.md | Obsidian publishing plugin setup, usage & architecture |
License
Private project. All rights reserved.
Project Gallery
This is the gallery album for the 'DevinMarshall.info — Digital Home of an Imagineer' project.
