Add one-click install script with automatic secret generation
- Create scripts/install.sh that auto-generates Ed25519 signing key, JWT secret, Postgres and MinIO passwords from .env.example placeholders - Add startup validation in server to reject placeholder secrets - Add 401 interceptor in console API client to clear stale JWT tokens - Update README Quick Start to use ./scripts/install.sh - Update .env.example with GENERATE_ME placeholders for all secrets Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
b29314fd80
commit
5153587baf
5 files changed
+170
-17
No files matched your search
+6
-6
@@ -1,18 +1,18 @@
|
||||
# PostgreSQL
|
||||
POSTGRES_USER=elydora
|
||||
POSTGRES_PASSWORD=elydora-secret-change-me
|
||||
POSTGRES_PASSWORD=GENERATE_ME
|
||||
POSTGRES_DB=elydora
|
||||
DATABASE_URL=postgresql://elydora:elydora-secret-change-me@postgres:5432/elydora
|
||||
DATABASE_URL=postgresql://elydora:GENERATE_ME@postgres:5432/elydora
|
||||
|
||||
# Redis
|
||||
REDIS_URL=redis://redis:6379
|
||||
|
||||
# MinIO
|
||||
MINIO_ROOT_USER=elydora
|
||||
MINIO_ROOT_PASSWORD=elydora-minio-secret-change-me
|
||||
MINIO_ROOT_PASSWORD=GENERATE_ME
|
||||
MINIO_ENDPOINT=http://minio:9000
|
||||
MINIO_ACCESS_KEY=elydora
|
||||
MINIO_SECRET_KEY=elydora-minio-secret-change-me
|
||||
MINIO_SECRET_KEY=GENERATE_ME
|
||||
MINIO_BUCKET=elydora-evidence
|
||||
|
||||
# Elydora API
|
||||
@@ -20,8 +20,8 @@ PORT=8787
|
||||
ENVIRONMENT=production
|
||||
API_VERSION=v1
|
||||
PROTOCOL_VERSION=1.0
|
||||
JWT_SECRET=change-me-to-a-random-string-at-least-32-chars
|
||||
ELYDORA_SIGNING_KEY=change-me-base64url-ed25519-private-key
|
||||
JWT_SECRET=GENERATE_ME
|
||||
ELYDORA_SIGNING_KEY=GENERATE_ME
|
||||
ALLOWED_ORIGINS=http://localhost:3000
|
||||
|
||||
# TSA (optional)
|
||||
|
||||
@@ -85,19 +85,27 @@ The result is a complete, verifiable history of everything your AI agents have e
|
||||
git clone https://github.com/Elydora-Infrastructure/Elydora-Open-Source.git
|
||||
cd Elydora-Open-Source
|
||||
|
||||
# Copy and edit environment configuration
|
||||
cp .env.example .env
|
||||
|
||||
# Generate Ed25519 signing keys for the server and initial admin
|
||||
./scripts/generate-keys.sh
|
||||
|
||||
# Start all services (API, PostgreSQL, MinIO, Redis)
|
||||
docker compose up -d
|
||||
|
||||
# Verify the server is healthy
|
||||
curl http://localhost:8787/v1/health
|
||||
# One-click install — generates all secrets and starts every service
|
||||
./scripts/install.sh
|
||||
```
|
||||
|
||||
The install script automatically:
|
||||
|
||||
1. Checks prerequisites (Docker, Docker Compose, OpenSSL, curl)
|
||||
2. Generates a `.env` with fresh cryptographic secrets (Ed25519 signing key, JWT secret, database and object-store passwords)
|
||||
3. Starts all services via `docker compose up -d`
|
||||
4. Waits for the API to become healthy
|
||||
|
||||
Once complete, open:
|
||||
|
||||
| Service | URL |
|
||||
|---------|-----|
|
||||
| Console | http://localhost:3000 |
|
||||
| API | http://localhost:8787 |
|
||||
| MinIO Console | http://localhost:9001 |
|
||||
|
||||
> **Re-running is safe.** If `.env` already exists, the script skips secret generation and only restarts services.
|
||||
|
||||
### Kubernetes Deploy
|
||||
|
||||
```bash
|
||||
|
||||
@@ -64,6 +64,14 @@ async function request<T>(
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// On 401, clear stored tokens and redirect to login
|
||||
if (response.status === 401 && typeof window !== 'undefined') {
|
||||
localStorage.removeItem('elydora_token');
|
||||
localStorage.removeItem('elydora_display_name');
|
||||
localStorage.removeItem('elydora_email');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
let errorBody: ErrorResponse | null = null;
|
||||
try {
|
||||
errorBody = await response.json() as ErrorResponse;
|
||||
|
||||
@@ -43,6 +43,21 @@ const queue = new BullMQAdapter({
|
||||
port: Number(redisUrl.port) || 6379,
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Validate required secrets at startup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const requiredSecrets = [
|
||||
{ name: 'JWT_SECRET', value: process.env.JWT_SECRET },
|
||||
{ name: 'ELYDORA_SIGNING_KEY', value: process.env.ELYDORA_SIGNING_KEY },
|
||||
];
|
||||
for (const { name, value } of requiredSecrets) {
|
||||
if (!value || value.startsWith('change-me')) {
|
||||
console.error(`FATAL: ${name} is not configured. Run ./scripts/install.sh to generate secrets.`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Build the env bindings object
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
# ───────────────────────────────────────────────────────────
|
||||
# Elydora Open Source — One-click deployment
|
||||
# ───────────────────────────────────────────────────────────
|
||||
|
||||
printf '\n'
|
||||
printf ' ╔══════════════════════════════════════════╗\n'
|
||||
printf ' ║ Elydora Open Source Installer ║\n'
|
||||
printf ' ║ Self-hosted AI audit platform ║\n'
|
||||
printf ' ╚══════════════════════════════════════════╝\n'
|
||||
printf '\n'
|
||||
|
||||
# ── helpers ────────────────────────────────────────────────
|
||||
|
||||
fail() { printf '\n[ERROR] %s\n' "$1" >&2; exit 1; }
|
||||
info() { printf '[INFO] %s\n' "$1"; }
|
||||
ok() { printf '[OK] %s\n' "$1"; }
|
||||
|
||||
# Platform-aware sed in-place (macOS needs '')
|
||||
sedi() {
|
||||
if [ "$(uname)" = "Darwin" ]; then
|
||||
sed -i '' "$@"
|
||||
else
|
||||
sed -i "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 1. check prerequisites ────────────────────────────────
|
||||
|
||||
info "Checking prerequisites..."
|
||||
|
||||
command -v docker >/dev/null 2>&1 \
|
||||
|| fail "docker is not installed. See https://docs.docker.com/get-docker/"
|
||||
|
||||
docker compose version >/dev/null 2>&1 \
|
||||
|| fail "docker compose (v2) is not available. See https://docs.docker.com/compose/install/"
|
||||
|
||||
command -v openssl >/dev/null 2>&1 \
|
||||
|| fail "openssl is not installed."
|
||||
|
||||
command -v curl >/dev/null 2>&1 \
|
||||
|| fail "curl is not installed."
|
||||
|
||||
ok "All prerequisites satisfied."
|
||||
|
||||
# ── 2. generate .env ───────────────────────────────────────
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ -f .env ]; then
|
||||
info "Existing .env found — skipping secret generation."
|
||||
else
|
||||
info "Generating .env from .env.example..."
|
||||
|
||||
[ -f .env.example ] || fail ".env.example not found in project root."
|
||||
|
||||
cp .env.example .env
|
||||
|
||||
# Generate secrets
|
||||
PG_PASS=$(openssl rand -hex 24)
|
||||
MINIO_PASS=$(openssl rand -hex 24)
|
||||
JWT=$(openssl rand -hex 32)
|
||||
SIGNING_KEY=$(openssl genpkey -algorithm ed25519 2>/dev/null \
|
||||
| openssl pkcs8 -topk8 -nocrypt -outform DER 2>/dev/null \
|
||||
| tail -c 32 \
|
||||
| base64 \
|
||||
| tr '+/' '-_' \
|
||||
| tr -d '=\n')
|
||||
|
||||
[ -n "$PG_PASS" ] || fail "Failed to generate Postgres password."
|
||||
[ -n "$MINIO_PASS" ] || fail "Failed to generate MinIO password."
|
||||
[ -n "$JWT" ] || fail "Failed to generate JWT secret."
|
||||
[ -n "$SIGNING_KEY" ] || fail "Failed to generate Ed25519 signing key."
|
||||
|
||||
# Replace placeholders
|
||||
sedi "s|POSTGRES_PASSWORD=GENERATE_ME|POSTGRES_PASSWORD=${PG_PASS}|" .env
|
||||
sedi "s|elydora:GENERATE_ME@postgres|elydora:${PG_PASS}@postgres|" .env
|
||||
sedi "s|MINIO_ROOT_PASSWORD=GENERATE_ME|MINIO_ROOT_PASSWORD=${MINIO_PASS}|" .env
|
||||
sedi "s|MINIO_SECRET_KEY=GENERATE_ME|MINIO_SECRET_KEY=${MINIO_PASS}|" .env
|
||||
sedi "s|JWT_SECRET=GENERATE_ME|JWT_SECRET=${JWT}|" .env
|
||||
sedi "s|ELYDORA_SIGNING_KEY=GENERATE_ME|ELYDORA_SIGNING_KEY=${SIGNING_KEY}|" .env
|
||||
|
||||
chmod 600 .env
|
||||
|
||||
ok "Generated .env with fresh secrets (permissions 0600)."
|
||||
fi
|
||||
|
||||
# ── 3. docker compose up ──────────────────────────────────
|
||||
|
||||
info "Starting services with docker compose..."
|
||||
docker compose up -d
|
||||
|
||||
# ── 4. wait for API health ────────────────────────────────
|
||||
|
||||
info "Waiting for API to become healthy (up to 60 s)..."
|
||||
|
||||
elapsed=0
|
||||
while [ "$elapsed" -lt 60 ]; do
|
||||
if curl -sf http://localhost:8787/v1/health >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
elapsed=$((elapsed + 2))
|
||||
done
|
||||
|
||||
if [ "$elapsed" -ge 60 ]; then
|
||||
fail "API did not become healthy within 60 seconds. Run 'docker compose logs api' to diagnose."
|
||||
fi
|
||||
|
||||
# ── 5. done ───────────────────────────────────────────────
|
||||
|
||||
printf '\n'
|
||||
printf ' ╔══════════════════════════════════════════╗\n'
|
||||
printf ' ║ Elydora is running! ║\n'
|
||||
printf ' ╠══════════════════════════════════════════╣\n'
|
||||
printf ' ║ Console: http://localhost:3000 ║\n'
|
||||
printf ' ║ API: http://localhost:8787 ║\n'
|
||||
printf ' ║ MinIO Console: http://localhost:9001 ║\n'
|
||||
printf ' ╚══════════════════════════════════════════╝\n'
|
||||
printf '\n'
|
||||
Reference in new issue
Block a user