From 031a4aa2894b153cb2c4fecedc034f594e7f979b Mon Sep 17 00:00:00 2001 From: senior Date: Wed, 8 Jul 2026 07:36:42 +0000 Subject: [PATCH] Build the homepage: project cards grouped in 4 sections Replaces the placeholder index.html with a full personal-projects page listing all 17 projects across four sections (CipherDolls, decentralized apps, AI on GPU, Ethereum infrastructure) as cards with name, description, tech tags, and GitHub links. Adds styles.css (dark-first, light override, mobile-first responsive grid) and a .dockerignore to keep the build context clean. Updates the Dockerfile to copy styles.css alongside index.html. --- .dockerignore | 8 ++ Dockerfile | 2 +- index.html | 308 +++++++++++++++++++++++++++++++++++--------------- styles.css | 190 +++++++++++++++++++++++++++++++ 4 files changed, 418 insertions(+), 90 deletions(-) create mode 100644 .dockerignore create mode 100644 styles.css diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e988baa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.git +.gitea +.agents-workflow +Dockerfile +.dockerignore +AGENTS.md +README.md +*.md \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 84a4da7..05081c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM nginx:1.27-alpine LABEL org.opencontainers.image.title="ffaerber/homepage" \ org.opencontainers.image.source="https://git.ffaerber.duckdns.org/ffaerber/homepage" -COPY index.html /usr/share/nginx/html/index.html +COPY index.html styles.css /usr/share/nginx/html/ EXPOSE 80 diff --git a/index.html b/index.html index 29afbf0..99294f3 100644 --- a/index.html +++ b/index.html @@ -3,97 +3,227 @@ - ffaerber - + + ffaerber — projects + -
-

ffaerber

-

Homelab & self-hosting enthusiast

-

- Hi, I'm ffaerber. This is my corner of the internet — a self-hosted homepage - running on my homelab. I tinker with containers, Gitea, and anything that runs - on a quiet box in the corner. -

-

- More coming soon. In the meantime, you can find my code over on Gitea. -

- - + + +
+ + +
+
+

CipherDolls — AI companion platform

+

An AI companion platform spanning cloud, web, and physical IoT devices.

+ github.com/cipherdolls ↗ +
+
+ +
+

core

+

Backend API for the CipherDolls platform: wallet-signature auth, AI chat completions, TTS/STT, embeddings (pgvector), USDC payments on Base, and IoT device management over MQTT.

+
    +
  • Bun
  • Elysia
  • Prisma
  • PostgreSQL
  • Redis
  • MQTT
  • +
+ View on GitHub ↗ +
+ +
+

server

+

Minimal chat backend derived from core, stripped to the essentials: chat with a Character, optional TTS/STT — same architecture, no payments or IoT.

+
    +
  • Bun
  • Elysia
  • Prisma
  • PostgreSQL
  • +
+ View on GitHub ↗ +
+ +
+

webapp

+

Web frontend for the platform.

+
    +
  • React Router
  • TypeScript
  • Tailwind
  • +
+ View on GitHub ↗ +
+ +
+

stream-player

+

TTS audio streaming proxy — subscribes to Redis Pub/Sub and forwards audio chunks to WebSocket clients with a compact binary protocol.

+
    +
  • TypeScript
  • Redis
  • WebSocket
  • +
+ View on GitHub ↗ +
+ +
+

stream-recorder

+

WebSocket audio ingestion microservice — accepts raw WAV streams, converts to MP3 in-memory via ffmpeg, forwards to the backend.

+
    +
  • TypeScript
  • WebSocket
  • ffmpeg
  • +
+ View on GitHub ↗ +
+ +
+

firmware-watcher

+

ESP-IDF firmware for the SenseCAP Watcher companion device: BLE WiFi provisioning, MQTT events, MP3 playback through the onboard codec, LVGL UI on a 412×412 display, deep sleep.

+
    +
  • C
  • ESP-IDF
  • ESP32-S3
  • LVGL
  • +
+ View on GitHub ↗ +
+ +
+

firmware-m5sticks3

+

ESP-IDF firmware for the M5StickS3 (ESP32-S3) pocket companion device with display, audio codec, and IMU.

+
    +
  • C
  • ESP-IDF
  • ESP32-S3
  • +
+ View on GitHub ↗ +
+ +
+
+ + +
+
+

Decentralized apps — Gnosis Chain + Ethereum Swarm

+

dApps with on-chain logic and censorship-resistant Swarm storage/hosting.

+
+
+ +
+

SwarmChat

+

Decentralized 1:1 messenger and WebRTC video calling: wallet identity, on-chain contact registry, encrypted messages over Swarm PSS with feeds as offline outbox. Live at swarmchat.eth.

+
    +
  • Solidity
  • React
  • Swarm PSS
  • WebRTC
  • +
+ View on GitHub ↗ +
+ +
+

Pink Chainsaw

+

Decentralized imageboard: post images, comment, and vote with xBZZ; fees auto-top-up the poster's Swarm postage stamp so good content stays alive. A social-score system prices posting by reputation.

+
    +
  • Solidity
  • Foundry
  • React
  • Swarm
  • +
+ View on GitHub ↗ +
+ +
+

FreeeMarket

+

Multi-vendor marketplace where anyone runs their own shop and gets paid in stablecoins through on-chain escrow — deliberately fee-free and VAT-free.

+
    +
  • Solidity
  • Gnosis Chain
  • Swarm
  • +
+ View on GitHub ↗ +
+ +
+

WalletMatrix

+

Pivot-table view of a crypto wallet across every EVM chain — tokens as rows, chains as columns; drag one cell onto another to bridge or cross-chain swap. Static SPA deployable to Swarm.

+
    +
  • React
  • TypeScript
  • Vite
  • +
+ View on GitHub ↗ +
+ +
+

swarm-connect

+

React connect-button + wizard for Swarm on Gnosis: wallet → network → balances → Bee node → postage stamp, in one modal. Published on npm; demo hosted on Swarm itself.

+
    +
  • React
  • wagmi
  • npm package
  • +
+ View on GitHub ↗ +
+ +
+

opentab

+

Onchain app bootstrapped with create-onchain (Base).

+
    +
  • Next.js
  • TypeScript
  • +
+ View on GitHub ↗ +
+ +
+
+ + +
+
+

AI on GPU — self-hosted speech services

+

Dockerized speech AI for NVIDIA GPUs, tuned for the RTX 5090 / Blackwell generation.

+
+
+ +
+

whisper-cuda

+

OpenAI-Whisper-compatible ASR web service built for RTX 5090 (sm_120) — faster-whisper on CTranslate2/CUDA 12.8, no PyTorch.

+
    +
  • Python
  • FastAPI
  • CUDA
  • +
+ View on GitHub ↗ +
+ +
+

kokoro-cuda

+

Minimal Kokoro TTS API with CUDA: 49 voices in 9 languages, streaming WAV/PCM plus MP3/Opus/FLAC, web UI included.

+
    +
  • Python
  • FastAPI
  • CUDA
  • +
+ View on GitHub ↗ +
+ +
+

voxtral-cuda

+

Text-to-speech service running Mistral's Voxtral-4B-TTS with CUDA acceleration via vLLM-Omni.

+
    +
  • Python
  • vLLM
  • CUDA
  • +
+ View on GitHub ↗ +
+ +
+
+ + +
+
+

Ethereum infrastructure

+
+
+ +
+

stakingtracker

+

Self-hosted ETH staking rewards tracker and tax-report generator — reads from your own beacon node, persists daily history to SQLite, no external APIs or keys.

+
    +
  • TypeScript
  • Fastify
  • SQLite
  • +
+ View on GitHub ↗ +
+ +
+
+
+ + \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..4643f63 --- /dev/null +++ b/styles.css @@ -0,0 +1,190 @@ +:root { + --fg: #e6edf3; + --muted: #9198a1; + --bg: #0d1117; + --bg-elev: #161b22; + --accent: #58a6ff; + --border: #30363d; + --tag-bg: #1f2630; + --radius: 10px; + --maxw: 64rem; +} + +@media (prefers-color-scheme: light) { + :root { + --fg: #1f2328; + --muted: #57606a; + --bg: #ffffff; + --bg-elev: #f6f8fa; + --accent: #0969da; + --border: #d0d7de; + --tag-bg: #eaeef2; + } +} + +* { box-sizing: border-box; } +html, body { margin: 0; padding: 0; } + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + color: var(--fg); + background: var(--bg); + line-height: 1.6; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +.container { + width: 100%; + max-width: var(--maxw); + margin: 0 auto; + padding: 0 1.25rem; +} + +/* Header */ +.site-header { + padding: 2.5rem 0 1.5rem; + border-bottom: 1px solid var(--border); +} +.site-title { + font-size: clamp(1.75rem, 4vw, 2.5rem); + margin: 0 0 0.25rem; + letter-spacing: -0.02em; + font-weight: 700; +} +.site-tagline { + margin: 0; + color: var(--muted); + font-size: 1rem; + max-width: 48rem; +} + +/* Sections */ +.section { + padding: 2rem 0 0.5rem; + scroll-margin-top: 1rem; +} +.section-head { margin-bottom: 1.25rem; } +.section-title { + font-size: clamp(1.15rem, 2.5vw, 1.5rem); + margin: 0 0 0.35rem; + letter-spacing: -0.01em; + font-weight: 700; +} +.section-desc { + margin: 0 0 0.4rem; + color: var(--muted); + font-size: 0.95rem; + max-width: 48rem; +} +.section-link { + color: var(--accent); + text-decoration: none; + font-size: 0.9rem; + font-weight: 600; +} +.section-link:hover { text-decoration: underline; } + +/* Cards grid */ +.cards { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); + gap: 1rem; +} + +.card { + display: flex; + flex-direction: column; + background: var(--bg-elev); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1.1rem 1.15rem 1.15rem; + transition: border-color 0.15s ease, transform 0.15s ease; +} +.card:hover { border-color: var(--accent); } +.card:focus-within { border-color: var(--accent); } + +.card-name { + margin: 0 0 0.5rem; + font-size: 1.1rem; + font-weight: 700; + letter-spacing: -0.01em; +} +.card-desc { + margin: 0 0 0.85rem; + color: var(--muted); + font-size: 0.92rem; + flex: 1 1 auto; +} + +/* Tags */ +.tags { + list-style: none; + margin: 0 0 0.9rem; + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 0.35rem; +} +.tags li { + font-size: 0.75rem; + line-height: 1.4; + color: var(--muted); + background: var(--tag-bg); + border: 1px solid var(--border); + border-radius: 999px; + padding: 0.12rem 0.55rem; + white-space: nowrap; +} + +/* Card link */ +.card-link { + align-self: flex-start; + color: var(--accent); + text-decoration: none; + font-size: 0.9rem; + font-weight: 600; +} +.card-link:hover { text-decoration: underline; } + +/* Footer */ +.site-footer { + margin-top: 2.5rem; + padding: 1.5rem 0 2rem; + border-top: 1px solid var(--border); +} +.footer-links { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-bottom: 0.5rem; +} +.footer-links a { + color: var(--accent); + text-decoration: none; + font-weight: 600; + font-size: 0.95rem; +} +.footer-links a:hover { text-decoration: underline; } +.footer-meta { + margin: 0; + font-size: 0.85rem; + color: var(--muted); +} +.footer-meta a { color: var(--muted); text-decoration: underline; } + +/* Focus visibility */ +a:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 4px; +} + +/* Responsive tightening on small screens */ +@media (max-width: 30rem) { + .cards { + grid-template-columns: 1fr; + } + .site-header { padding: 1.75rem 0 1rem; } + .section { padding: 1.5rem 0 0.25rem; } +} \ No newline at end of file -- 2.54.0