Hypernova Site — Private GitHub Content Fetching for Confidential Memos
Confidential memos stream from a private repository through a fine-grained token, with the file path derived automatically from the URL slug. None of that content ever touches the deployed build.
Changelog - 2025-12-12 (#3)
Hypernova Site: Private GitHub Content Fetching for Confidential Memos
Overview
Implemented a secure content fetching system that retrieves confidential investment memos from a private GitHub repository (lossless-group/hypernova-secure-data) at runtime. This keeps sensitive content out of deployed static assets while allowing authorized users to view memos after passcode authentication.
Key Achievement: Content is now fetched directly from a private GitHub repo using a fine-grained PAT, with automatic path derivation from URL slugs to GitHub file paths.
Architecture
User Request GitHub Private Repo
│ │
▼ │
/memos/Example-Company-v002-draft │
│ │
▼ ▼
[Auth Check] ──────────────► [fetchMemoBySlug()]
│ │
│ ▼
│ [deriveGitHubPathFromSlug()]
│ │
│ ▼
│ deals/Example-Company/outputs/Example-Company-v0.0.2/
│ Example-Company-v0.0.2-draft.md
│ │
▼ ▼
[Render Memo] ◄───────────── [Raw GitHub Content]Changes by Area
1. New Library: src/lib/github-content.ts
Core library for fetching content from private GitHub repositories:
Features:
fetchGitHubContent()— Fetch raw markdown from GitHub via PAT authenticationlistGitHubDirectory()— List files in a GitHub directoryfetchMemoBySlug()— High-level function to fetch memos by URL slugderiveGitHubPathFromSlug()— Convert URL slugs to GitHub file pathsparseFrontmatter()— Extract YAML frontmatter from markdownisLocalDemoMode()— Check if running in local fallback mode5-minute in-memory cache to reduce API calls
URL-to-Path Conversion:
URL Slug: Example-Company-v002-draft
GitHub Path: deals/Example-Company/outputs/Example-Company-v0.0.2/Example-Company-v0.0.2-draft.md
URL Slug: Second-Example-v003-draft
GitHub Path: deals/Second-Example/outputs/Second-Example-v0.0.3/Second-Example-v0.0.3-draft.mdThe system converts URL-safe versions (v002) to dotted versions (v0.0.2) automatically.
2. Updated Memo Page: src/pages/memos/[slug].astro
SSR page that fetches memo content at runtime:
import { fetchMemoBySlug, isLocalDemoMode } from '@lib/github-content';
export const prerender = false;
// Auth check
const accessCookie = Astro.cookies.get('universal_portfolio_access');
if (!accessCookie?.value) {
return Astro.redirect(`/portfolio-gate?redirect=${redirectPath}`);
}
// Fetch from GitHub (or local fallback)
const memo = await fetchMemoBySlug(slug);Features:
Requires passcode authentication via
/portfolio-gateShows "Local Demo Mode" banner when PAT not configured
Logs fetch mode to server console for debugging
3. Local Demo Mode
When GITHUB_CONTENT_PAT is empty, the system falls back to reading from src/content/markdown-memos/ for local testing:
Yellow banner: "Local Demo Mode — Content loaded from local files"
Server logs:
[memos] LOCAL DEMO MODE - fetching "slug" from src/content/markdown-memos/
4. Configuration
Environment Variables (.env):
GITHUB_CONTENT_PAT=github_pat_xxxxx # Fine-grained PAT with Contents read-only
GITHUB_CONTENT_OWNER=lossless-group
GITHUB_CONTENT_REPO=hypernova-secure-data
GITHUB_CONTENT_BRANCH=mainTypeScript Path Alias (tsconfig.json):
"@lib/*": ["src/lib/*"]Files Changed Summary
New
src/lib/github-content.ts— GitHub content fetching library with caching, path derivation, and local fallback
Modified
src/pages/memos/[slug].astro— Now usesfetchMemoBySlug()from the new librarytsconfig.json— Added@lib/*path alias.env— Added GitHub content configuration variables
Security Considerations
Fine-grained PAT: Only needs
Contents: Read-onlypermission on the single repositoryServer-side only: PAT is never exposed to client-side code
SSR required: Page uses
export const prerender = falseto fetch at runtimeAuth gated: Memo pages require passcode authentication before content loads
Testing
Local Demo Mode:
# Leave GITHUB_CONTENT_PAT empty in .env
pnpm dev
# Visit: http://localhost:4321/memos/Example-Company-v002-draft
# Should see yellow "Local Demo Mode" bannerGitHub Mode:
# Add PAT to .env
pnpm dev
# Visit: http://localhost:4321/memos/Second-Example-v005-draft
# No banner, content from GitHub
# Server console: [memos] GITHUB MODE - fetching...Notes / Follow-Ups
For production (Vercel), add
GITHUB_CONTENT_PATas an environment variablePAT expiration: Set reminders to rotate before expiry
Consider adding a cache-busting mechanism for content updates
The path derivation assumes the standard structure:
deals/{Company}/outputs/{Company}-{version}/{slug}.md
