Infrastructure Principles
Coalition technical infrastructure must be:
| Principle | Rationale |
|---|---|
| Resilient | Must survive traffic spikes and attacks |
| Private | Cannot feed surveillance ecosystems |
| Accessible | Works on low-bandwidth connections |
| Maintainable | Multiple organizations can contribute |
| Affordable | Sustainable on nonprofit budgets |
Static Site Architecture
Why Static Sites
Static sites pre-compile all content into HTML/CSS/JS files, eliminating:
| Eliminated Risk | Benefit |
|---|---|
| Database queries | No SQL injection attacks |
| Server-side processing | Handles unlimited traffic |
| Backend vulnerabilities | Minimal attack surface |
| Server maintenance | No patches to apply |
| Hosting costs | Free/low-cost options |
Recommended Static Site Generators
| Generator | Build Speed | Best For | Learning Curve |
|---|---|---|---|
| Hugo | Sub-second | Large sites, rapid updates | Moderate |
| 11ty (Eleventy) | Fast | Flexible templating | Low |
| Jekyll | Moderate | GitHub Pages native | Low |
| Astro | Fast | Modern components | Moderate |
| Gatsby | Slow | React ecosystem | High |
11ty for Advocacy Sites
Recommended because:
- Markdown-first content (non-developers can contribute)
- Flexible templating (Nunjucks, Liquid, etc.)
- Zero client-side JavaScript by default
- Native SCSS support
- Simple GitHub Actions deployment
Hosting Options
Comparison
| Platform | Cost | Features | Best For |
|---|---|---|---|
| GitHub Pages | Free | Native Git integration | Simple sites |
| Netlify | Free tier | Build previews, forms | Feature-rich |
| Vercel | Free tier | Edge functions | Modern apps |
| Cloudflare Pages | Free | Integrated DDoS | Security-focused |
| Render | Free tier | Background jobs | Dynamic features |
GitHub Pages Setup
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- uses: peaceiris/actions-gh-pages@v4
with:
github_token: $
publish_dir: ./_site
Netlify Configuration
# netlify.toml
[build]
command = "npm run build"
publish = "_site"
[build.environment]
NODE_VERSION = "20"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Content-Security-Policy = "default-src 'self'"
CDN Configuration
Cloudflare Project Galileo
Free enterprise-grade protection for qualifying organizations:
| Feature | Normal Cost | Galileo |
|---|---|---|
| DDoS mitigation | $3,000+/mo | Free |
| Web Application Firewall | $200+/mo | Free |
| Global CDN | $200+/mo | Free |
| SSL certificates | Included | Free |
| Analytics | $20+/mo | Free |
Application Process
- Visit cloudflare.com/galileo
- Submit organization details
- Demonstrate qualifying purpose (human rights, civil society)
- Wait for approval (1-2 weeks typical)
- Configure DNS to use Cloudflare
Essential Cloudflare Settings
| Setting | Value | Purpose |
|---|---|---|
| SSL/TLS | Full (strict) | End-to-end encryption |
| Always Use HTTPS | On | Force secure connections |
| Minimum TLS | 1.2 | Block outdated protocols |
| Automatic HTTPS Rewrites | On | Fix mixed content |
| Browser Integrity Check | On | Block bad bots |
| Hotlink Protection | On | Prevent bandwidth theft |
"Under Attack" Mode
Activate during DDoS or traffic surge:
# Enable via API
curl -X PATCH \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/security_level" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{"value":"under_attack"}'
What it does:
- Shows JavaScript challenge to all visitors
- Blocks automated attacks
- Legitimate users proceed after ~5 seconds
- Activate automatically via webhook on monitoring alert
Cache Configuration
Cache Headers
# For static assets (1 year)
location ~* \.(css|js|woff2|png|jpg|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# For HTML (short cache for updates)
location ~* \.html$ {
expires 10m;
add_header Cache-Control "public, must-revalidate";
}
Cloudflare Page Rules
| Rule | Pattern | Setting |
|---|---|---|
| Cache everything | *.yoursite.org/* |
Cache Level: Cache Everything |
| Edge TTL | *.yoursite.org/* |
Edge Cache TTL: 1 month |
| Browser TTL | *.yoursite.org/*.html |
Browser Cache TTL: 10 minutes |
| Bypass cache | *.yoursite.org/api/* |
Cache Level: Bypass |
Asset Versioning
Use content hashes for cache busting:
// eleventy.config.js
const crypto = require('crypto');
const fs = require('fs');
module.exports = function(eleventyConfig) {
eleventyConfig.addFilter('hash', function(filepath) {
const content = fs.readFileSync(`_site${filepath}`);
return crypto.createHash('md5')
.update(content)
.digest('hex')
.slice(0, 8);
});
};
<!-- Usage in template -->
<link rel="stylesheet" href="/css/main.css?v={{ '/css/main.css' | hash }}">
Custom Domain Management
DNS Configuration
| Record Type | Name | Value | Purpose |
|---|---|---|---|
| A | @ | GitHub Pages IPs | Root domain |
| CNAME | www | username.github.io | Subdomain |
| TXT | @ | Verification record | Ownership proof |
| CAA | @ | 0 issue "letsencrypt.org" | Certificate authority |
DNSSEC
Enable DNSSEC to prevent DNS hijacking:
- Enable in Cloudflare dashboard
- Add DS record at registrar
- Verify propagation
Multiple Domains
For redundancy, maintain backup domains:
| Domain | Purpose | Registrar |
|---|---|---|
| primary.org | Main site | Registrar A |
| primary-backup.org | Backup access | Registrar B |
| primaryinfo.org | Alternative | Registrar C |
SSL/TLS Configuration
Certificate Options
| Provider | Cost | Automation | Best For |
|---|---|---|---|
| Let's Encrypt | Free | Certbot | Self-hosted |
| Cloudflare | Free | Automatic | CDN-fronted |
| AWS ACM | Free | AWS integration | AWS hosting |
| DigiCert | $$ | Enterprise | Compliance needs |
Security Headers
<!-- In base template -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;">
Recommended Headers
| Header | Value | Purpose |
|---|---|---|
| Strict-Transport-Security | max-age=31536000; includeSubDomains | Force HTTPS |
| X-Content-Type-Options | nosniff | Prevent MIME sniffing |
| X-Frame-Options | DENY | Prevent clickjacking |
| Referrer-Policy | strict-origin-when-cross-origin | Limit referrer data |
| Permissions-Policy | geolocation=() | Disable dangerous APIs |
Analytics and Monitoring
Privacy-Preserving Analytics
| Platform | Privacy Features | Cost |
|---|---|---|
| Plausible | No cookies, IP hashing | $9/mo+ |
| Fathom | No cookies, GDPR compliant | $14/mo+ |
| Umami | Open source, self-hosted | Free |
| Simple Analytics | No cookies, no fingerprinting | $19/mo+ |
Plausible Setup
<!-- Add to base template -->
<script defer
data-domain="yoursite.org"
src="https://plausible.io/js/script.js">
</script>
Uptime Monitoring
| Service | Features | Cost |
|---|---|---|
| UptimeRobot | HTTP checks, alerts | Free tier |
| Better Uptime | Status pages, incidents | Free tier |
| Datadog | Full observability | $15/host/mo |
| Pingdom | Global monitoring | $10/mo |
Alert Configuration
# Example webhook for Slack/Discord
alerts:
- type: http_check
url: https://yoursite.org
interval: 60 # seconds
- type: ssl_expiry
domain: yoursite.org
days_warning: 30
- type: response_time
url: https://yoursite.org
threshold_ms: 3000
Development Workflows
Git Workflow for Coalitions
Use GitHub Flow (trunk-based development):
main ────●────●────●────●────●────●────
\ \ \ \ \
● ● ● ● ●
feature fix content update
Branch Protection Rules
| Rule | Setting | Purpose |
|---|---|---|
| Require PR | Yes | No direct pushes to main |
| Require review | 1+ approvals | Quality control |
| Require status checks | Build must pass | Prevent broken deploys |
| Require linear history | Optional | Clean commit history |
| Include administrators | Yes | No exceptions |
Team Structure
@coalition/admins # Full access
@coalition/developers # Code changes
@coalition/legal-review # Approve legal content
@coalition/translators # Translation changes
@coalition/content # Markdown content
PR Template
<!-- .github/pull_request_template.md -->
## Description
<!-- What does this PR do? -->
## Type of Change
- [ ] Content update
- [ ] Bug fix
- [ ] New feature
- [ ] Translation
## Checklist
- [ ] Content is legally accurate (if applicable)
- [ ] Translations updated (if applicable)
- [ ] Mobile responsive verified
- [ ] Accessibility checked
- [ ] Build passes locally
Content Contribution Protocol
For non-developers adding content:
- Edit in GitHub - Use web interface for Markdown
- Create branch - Automatic when editing
- Submit PR - Request review
- Review process - Legal + comms approval
- Merge - Automatic deployment
Automated Deployment
GitHub Actions for 11ty
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build site
run: npm run build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: _site
deploy:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: $
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Preview Deployments
For PR previews (Netlify/Vercel):
# netlify.toml
[context.deploy-preview]
command = "npm run build"
[context.branch-deploy]
command = "npm run build"
Error Handling
Custom Error Pages
<!-- src/404.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Page Not Found - Coalition Name</title>
</head>
<body>
<h1>Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<p><a href="/">Return to homepage</a></p>
<p><strong>Need immediate help?</strong> Call our hotline: XXX-XXX-XXXX</p>
</body>
</html>
Error Monitoring
| Service | Features | Cost |
|---|---|---|
| Sentry | Error tracking, alerts | Free tier |
| LogRocket | Session replay | $99/mo |
| Rollbar | Error aggregation | Free tier |
Security Monitoring
Vulnerability Scanning
# .github/workflows/security.yml
name: Security Scan
on:
schedule:
- cron: '0 0 * * 0' # Weekly
push:
branches: [main]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run npm audit
run: npm audit --audit-level=high
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: $
Dependency Updates
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
Technical Stack Summary
Recommended Stack
| Layer | Tool | Cost |
|---|---|---|
| Static Site Generator | 11ty (Eleventy) | Free |
| Hosting | GitHub Pages | Free |
| CDN/DDoS | Cloudflare Galileo | Free |
| SSL | Cloudflare (auto) | Free |
| DNS | Cloudflare | Free |
| Analytics | Plausible | $9/mo |
| Uptime | UptimeRobot | Free |
| CI/CD | GitHub Actions | Free |
| Version Control | GitHub | Free |
Total monthly cost: ~$9 for production-ready, resilient infrastructure
Infrastructure Checklist
Initial Setup
- [ ] Static site generator configured
- [ ] GitHub repository created
- [ ] Branch protection rules enabled
- [ ] GitHub Actions workflow added
- [ ] Custom domain configured
- [ ] Cloudflare Galileo applied
- [ ] SSL/TLS configured
- [ ] Security headers added
Monitoring
- [ ] Uptime monitoring configured
- [ ] SSL expiry alerts enabled
- [ ] Analytics installed
- [ ] Error tracking configured
- [ ] Dependency scanning enabled
Security
- [ ] DNSSEC enabled
- [ ] CAA records set
- [ ] Security headers verified
- [ ] Backup domains registered
- [ ] "Under Attack" mode tested
Team
- [ ] Team permissions configured
- [ ] PR template created
- [ ] Contribution guide documented
- [ ] Review process established
Next Steps
- Review implementation roadmap for phased deployment
- Set up crisis communication infrastructure
- Configure CRM tools for organizing