mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
Add EC2 recovery backup: NixOS config, AppFlowy compose/env template, credential server bootstrap, recovery guide
- ec2-configuration.nix: full NixOS config for aws-nixos-node-1 - docker-compose.minimal.yml: AppFlowy Cloud compose with search_path fix - .env.example: sanitized AppFlowy env template - nixos-setup-cred-server.sh: credential server bootstrap - RECOVERY.md: step-by-step rebuild instructions - .gitignore: secrets dir excluded - credential_provider.py reverted to repo HEAD (EC2 had hardcoded AWS creds) - racknerd_root.txt removed from working tree
This commit is contained in:
parent
ba1e4cf191
commit
d4603644d2
6 changed files with 318 additions and 0 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -217,3 +217,7 @@ data
|
|||
|
||||
# Rust build artifacts
|
||||
**/target/
|
||||
|
||||
# Recovery secrets backup - DO NOT COMMIT
|
||||
4-Infrastructure/infra/secrets/
|
||||
|
||||
|
|
|
|||
25
4-Infrastructure/infra/RECOVERY.md
Normal file
25
4-Infrastructure/infra/RECOVERY.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Recovery Guide
|
||||
|
||||
If EC2 server aws-nixos-node-1 (100.69.1.43) dies, rebuild from these files.
|
||||
|
||||
## Critical Files (committed to repo)
|
||||
- `ec2-configuration.nix` — Full NixOS config (Caddy routes, AppFlowy, Forgejo, credential server)
|
||||
- `docker-compose.minimal.yml` — Custom AppFlowy Cloud compose (in 5-Applications/AppFlowy-Cloud/)
|
||||
- `nixos-setup-cred-server.sh` — Credential server bootstrap
|
||||
- `credential_provider.py` — Credential resolution chain
|
||||
- `credential_server.py` — Webhook handler
|
||||
- `.env.example` — Sanitized AppFlowy Cloud env template
|
||||
|
||||
## Critical Files (NOT committed — in `secrets/` dir, gitignored)
|
||||
- `credentials.json` — All 8 API provider keys
|
||||
- `appflowy.env` — AppFlowy Cloud env (RDS host, JWT, encryption key)
|
||||
- `tailscale-auth.key` — Tailscale auth key
|
||||
|
||||
## Recovery Flow
|
||||
1. Launch new NixOS EC2 → copy `ec2-configuration.nix` to `/etc/nixos/`
|
||||
2. Restore secrets from gitignored backup
|
||||
3. `nixos-rebuild switch` — brings up Caddy, credential server, Forgejo, Heimdall
|
||||
4. Deploy AppFlowy: copy compose + `.env` to `/var/lib/AppFlowy-Cloud/`, start stack
|
||||
5. RDS (2,685 records) survives independently — reconnect AppFlowy to it
|
||||
6. Forgejo repos lost unless backed up separately
|
||||
7. Heimdall tiles lost unless backed up separately
|
||||
124
4-Infrastructure/infra/ec2-configuration.nix
Normal file
124
4-Infrastructure/infra/ec2-configuration.nix
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
{ config, pkgs, ... }:
|
||||
{
|
||||
imports = [ <nixpkgs/nixos/modules/virtualisation/amazon-image.nix> ];
|
||||
|
||||
networking.hostName = "aws-nixos-node-1";
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
|
||||
documentation.enable = false;
|
||||
documentation.nixos.enable = false;
|
||||
documentation.man.enable = false;
|
||||
documentation.info.enable = false;
|
||||
documentation.doc.enable = false;
|
||||
|
||||
nix.settings.auto-optimise-store = true;
|
||||
nix.gc = {
|
||||
automatic = true;
|
||||
dates = "weekly";
|
||||
options = "--delete-older-than 7d";
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [ tailscale python3 ];
|
||||
|
||||
services.tailscale.enable = true;
|
||||
services.tailscale.authKeyFile = "/etc/tailscale-auth.key";
|
||||
services.tailscale.extraUpFlags = [ "--ssh" ];
|
||||
|
||||
virtualisation.podman.enable = true;
|
||||
|
||||
virtualisation.oci-containers = {
|
||||
backend = "podman";
|
||||
containers = {
|
||||
heimdall = {
|
||||
image = "lscr.io/linuxserver/heimdall:latest";
|
||||
autoStart = true;
|
||||
ports = [ "127.0.0.1:8090:80" ];
|
||||
volumes = [ "/var/lib/heimdall:/config" ];
|
||||
environment = {
|
||||
PUID = "1000";
|
||||
PGID = "1000";
|
||||
TZ = "America/Chicago";
|
||||
};
|
||||
};
|
||||
forgejo = {
|
||||
image = "codeberg.org/forgejo/forgejo:1.21";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"0.0.0.0:3000:3000"
|
||||
];
|
||||
volumes = [
|
||||
"/var/lib/forgejo/data:/data"
|
||||
];
|
||||
environment = {
|
||||
USER_UID = "1000";
|
||||
USER_GID = "1000";
|
||||
FORGEJO__server__DOMAIN = "researchstack.info";
|
||||
FORGEJO__server__ROOT_URL = "https://researchstack.info/git/";
|
||||
FORGEJO__server__HTTP_PORT = "3000";
|
||||
FORGEJO__server__SSH_PORT = "22";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
extraConfig = ''
|
||||
researchstack.info {
|
||||
handle_path /api/* {
|
||||
reverse_proxy localhost:8444
|
||||
}
|
||||
handle_path /git/* {
|
||||
reverse_proxy localhost:3000
|
||||
}
|
||||
handle_path /appflowy/* {
|
||||
reverse_proxy localhost:8000
|
||||
}
|
||||
handle {
|
||||
basic_auth {
|
||||
admin $2b$12$yUaZ1sxezq84rRDQ3Fa48OdTx52vU7bldIfmav4cwsRn227pFJQDq
|
||||
}
|
||||
reverse_proxy localhost:8090
|
||||
}
|
||||
}
|
||||
|
||||
# git.researchstack.info subdomain removed - use /git/* path instead
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.services.rs-credential-server = {
|
||||
description = "Research Stack Credential Server";
|
||||
after = [ "network-online.target" "tailscaled.service" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "root";
|
||||
WorkingDirectory = "/opt/rs-surface";
|
||||
ExecStart = "${pkgs.python3}/bin/python3 /opt/rs-surface/credential_server.py --port 8444 --bind 127.0.0.1";
|
||||
Restart = "always";
|
||||
RestartSec = "5";
|
||||
Environment = [
|
||||
"RS_CREDENTIAL_CONFIG=/etc/rs-surface/credentials.json"
|
||||
"RS_CREDENTIAL_SERVER=http://100.101.247.127:8444"
|
||||
"RS_SURFACE_NODE_ID=aws-nixos-node-1"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
systemd.services.appflowy-cloud = {
|
||||
description = "AppFlowy Cloud podman-compose stack";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = with pkgs; [ podman podman-compose ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
WorkingDirectory = "/var/lib/AppFlowy-Cloud";
|
||||
ExecStart = "${pkgs.podman-compose}/bin/podman-compose -f docker-compose.minimal.yml up -d";
|
||||
ExecStop = "${pkgs.podman-compose}/bin/podman-compose -f docker-compose.minimal.yml down";
|
||||
};
|
||||
};
|
||||
}
|
||||
39
4-Infrastructure/infra/nixos-setup-cred-server.sh
Normal file
39
4-Infrastructure/infra/nixos-setup-cred-server.sh
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Create unit file
|
||||
cat > /etc/systemd/system/rs-credential-server.service << 'UNITEOF'
|
||||
[Unit]
|
||||
Description=Research Stack Credential Server
|
||||
After=network-online.target tailscaled.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/rs-surface
|
||||
ExecStart=/run/current-system/sw/bin/python3 /opt/rs-surface/credential_server.py --port 8444 --bind 0.0.0.0
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Environment=RS_CREDENTIAL_CONFIG=/etc/rs-surface/credentials.json
|
||||
Environment=RS_CREDENTIAL_SERVER=http://100.101.247.127:8444
|
||||
Environment=RS_SURFACE_NODE_ID=aws-nixos-node-1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
UNITEOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable rs-credential-server
|
||||
systemctl restart rs-credential-server
|
||||
|
||||
echo "Service status:"
|
||||
systemctl status rs-credential-server --no-pager --lines=5
|
||||
echo ""
|
||||
echo "Testing health endpoint..."
|
||||
curl -sf --connect-timeout 5 http://localhost:8444/health && echo "OK" || echo "FAIL"
|
||||
|
||||
# Test remote credential resolution
|
||||
echo ""
|
||||
echo "Testing remote credential resolution..."
|
||||
curl -sf --connect-timeout 10 http://localhost:8444/credentials/deepseek | python3 -m json.tool
|
||||
41
5-Applications/AppFlowy-Cloud/.env.example
Normal file
41
5-Applications/AppFlowy-Cloud/.env.example
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# AppFlowy Cloud - Minimal Configuration
|
||||
APPFLOWY_BASE_URL=http://researchstack.info/appflowy
|
||||
APPFLOWY_WS_BASE_URL=ws://researchstack.info/appflowy/ws/v1
|
||||
|
||||
# External RDS
|
||||
RDS_HOST=database-1-instance-1.cghu8yqogqwo.us-east-1.rds.amazonaws.com
|
||||
RDS_PORT=5432
|
||||
RDS_DB=postgres
|
||||
RDS_USER=postgres
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
|
||||
# Storage (local filesystem via minio)
|
||||
APPFLOWY_S3_ACCESS_KEY=CHANGEME
|
||||
APPFLOWY_S3_SECRET_KEY=CHANGEME
|
||||
APPFLOWY_S3_BUCKET=appflowy
|
||||
|
||||
# Auth
|
||||
GOTRUE_ADMIN_EMAIL=admin@researchstack.info
|
||||
GOTRUE_ADMIN_PASSWORD=CHANGEME
|
||||
GOTRUE_BASE_URL=http://researchstack.info/appflowy/gotrue
|
||||
GOTRUE_SITE_URL=http://researchstack.info
|
||||
GOTRUE_DISABLE_SIGNUP=false
|
||||
|
||||
# Encryption
|
||||
APPFLOWY_ENCRYPTION_KEY=CHANGE_THIS
|
||||
APPFLOWY_JWT_SECRET=CHANGE_THIS
|
||||
|
||||
# External URLs (for Caddy proxying)
|
||||
API_EXTERNAL_URL=https://researchstack.info/appflowy/gotrue
|
||||
APPFLOWY_WEB_URL=https://researchstack.info/appflowy
|
||||
APPFLOWY_DATABASE_URL=postgres://postgres@database-1-instance-1.cghu8yqogqwo.us-east-1.rds.amazonaws.com:5432/postgres?sslmode=require
|
||||
GOTRUE_JWT_SECRET=${APPFLOWY_JWT_SECRET}
|
||||
GOTRUE_DATABASE_URL=${APPFLOWY_DATABASE_URL}
|
||||
APPFLOWY_GOTRUE_BASE_URL=http://gotrue:9999
|
||||
APPFLOWY_GOTRUE_JWT_SECRET=${APPFLOWY_JWT_SECRET}
|
||||
APPFLOWY_S3_CREATE_BUCKET=false
|
||||
APPFLOWY_S3_USE_MINIO=false
|
||||
GOTRUE_MAILER_AUTOCONFIRM=true
|
||||
85
5-Applications/AppFlowy-Cloud/docker-compose.minimal.yml
Normal file
85
5-Applications/AppFlowy-Cloud/docker-compose.minimal.yml
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: appflowy_local
|
||||
POSTGRES_DB: appflowy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
gotrue:
|
||||
image: docker.io/appflowyinc/gotrue:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- GOTRUE_ADMIN_EMAIL=admin@researchstack.info
|
||||
- GOTRUE_ADMIN_PASSWORD=changeme789
|
||||
- GOTRUE_DISABLE_SIGNUP=false
|
||||
- GOTRUE_SITE_URL=https://researchstack.info/appflowy
|
||||
- GOTRUE_URI_ALLOW_LIST=**
|
||||
- GOTRUE_JWT_SECRET=${APPFLOWY_JWT_SECRET:-changeme_jwt_secret_at_least_32_chars_long}
|
||||
- GOTRUE_JWT_EXP=3600
|
||||
- GOTRUE_DB_DRIVER=postgres
|
||||
- API_EXTERNAL_URL=https://researchstack.info/appflowy/gotrue
|
||||
- DATABASE_URL=postgres://postgres:appflowy_local@postgres:5432/appflowy?sslmode=disable&options=-c%20search_path%3Dauth%2Cpublic
|
||||
- PORT=9999
|
||||
- GOTRUE_MAILER_AUTOCONFIRM=true
|
||||
- GOTRUE_RATE_LIMIT_EMAIL_SENT=100
|
||||
- GOTRUE_LOG_LEVEL=info
|
||||
- GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin
|
||||
- REDIS_ENABLED=true
|
||||
- REDIS_URL=redis://redis:6379
|
||||
|
||||
appflowy_cloud:
|
||||
image: docker.io/appflowyinc/appflowy_cloud:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
gotrue:
|
||||
condition: service_started
|
||||
environment:
|
||||
- APPFLOWY_ENVIRONMENT=production
|
||||
- APPFLOWY_DATABASE_URL=postgres://postgres:appflowy_local@postgres:5432/appflowy?sslmode=disable
|
||||
- APPFLOWY_REDIS_URI=redis://redis:6379
|
||||
- APPFLOWY_GOTRUE_JWT_SECRET=${APPFLOWY_JWT_SECRET:-changeme_jwt_secret_at_least_32_chars_long}
|
||||
- APPFLOWY_GOTRUE_JWT_EXP=3600
|
||||
- APPFLOWY_GOTRUE_BASE_URL=http://gotrue:9999
|
||||
- APPFLOWY_BASE_URL=https://researchstack.info/appflowy
|
||||
- APPFLOWY_WS_BASE_URL=wss://researchstack.info/appflowy/ws/v1
|
||||
- APPFLOWY_WEB_URL=https://researchstack.info/appflowy
|
||||
- APPFLOWY_ENCRYPTION_KEY=${APPFLOWY_ENCRYPTION_KEY:-changeme_encryption_key_at_least_32_chars_long}
|
||||
- APPFLOWY_JWT_SECRET=${APPFLOWY_JWT_SECRET:-changeme_jwt_secret_at_least_32_chars_long}
|
||||
- APPFLOWY_S3_CREATE_BUCKET=false
|
||||
- APPFLOWY_S3_USE_MINIO=false
|
||||
- APPFLOWY_ACCESS_CONTROL=false
|
||||
|
||||
- APPFLOWY_S3_ENABLED=false
|
||||
- APPFLOWY_SEARCH_ENABLED=false
|
||||
- RUST_LOG=info
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000"
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
Loading…
Add table
Reference in a new issue