Saltar a contenido

Deployment

This guide covers deploying the CyberEco Hub app to production and publishing the npm packages.

Production Checklist

Before deploying, verify the following:

  • All environment variables are set (see Environment Variables)
  • Firebase project is created and configured
  • Firebase Authentication is enabled (Email/Password provider)
  • Firestore database is created with appropriate security rules
  • JWT_SECRET is a strong, random string (at least 32 characters)
  • PUBLIC_HUB_URL points to your production domain
  • Build completes without errors: npm run build
  • All tests pass: npm run test
  • Type checking passes: npm run type-check

Environment Variables

Set all required variables for your production environment:

Production environment
# Public (exposed to client)
PUBLIC_HUB_URL=https://hub.cybereco.app
PUBLIC_FIREBASE_API_KEY=AIzaSy...
PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
PUBLIC_FIREBASE_PROJECT_ID=your-project-id
PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
PUBLIC_FIREBASE_MESSAGING_SENDER_ID=123456789
PUBLIC_FIREBASE_APP_ID=1:123456789:web:abc123

# Server-only
JWT_SECRET=your-production-secret-at-least-32-chars

Secrets management

Never commit secrets to version control. Use your hosting platform's secrets management:

  • Vercel: Environment Variables in project settings
  • Railway: Variables tab in service settings
  • AWS: Systems Manager Parameter Store or Secrets Manager
  • Docker: Docker secrets or environment files excluded from Git

Building for Production

Build All Packages

npm run build

Turborepo builds all 5 packages in topological dependency order:

  1. @cyber-eco/types (zero deps)
  2. @cyber-eco/firebase + @cyber-eco/auth (in parallel, depend on types)
  3. @cyber-eco/services (depends on types + auth)
  4. apps/hub (depends on all packages)

Build Output

Package Build Tool Output
@cyber-eco/types tsup dist/ (ESM + CJS + .d.ts)
@cyber-eco/firebase tsup dist/ (ESM + CJS + .d.ts)
@cyber-eco/auth tsup dist/ (ESM + CJS + .d.ts)
@cyber-eco/services tsup dist/ (ESM + CJS + .d.ts)
apps/hub Astro/Vite dist/ (server + client bundles)

Verify the Build

# Check for zero Firebase imports in services
grep -r "firebase" packages/services/src/ --include="*.ts" --include="*.tsx"
# Should return zero results

# Check build output exists
ls apps/hub/dist/

Deploying the Hub App

The Hub app is built with Astro 5 using the @astrojs/node adapter for server-side rendering. It can be deployed to any platform that supports Node.js.

Astro + Node Adapter

The Hub produces a standalone Node.js server in apps/hub/dist/. The entry point is apps/hub/dist/server/entry.mjs.

cd apps/hub
node dist/server/entry.mjs

The server listens on the port specified by the PORT environment variable (default: 4321).

Dockerfile
FROM node:20-slim AS builder
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build

FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/apps/hub/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/apps/hub/package.json ./package.json

ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321

CMD ["node", "dist/server/entry.mjs"]

Build and run:

docker build -t cybereco-hub .
docker run -p 4321:4321 --env-file .env cybereco-hub
  1. Connect your GitHub repository
  2. Set the Root Directory to apps/hub
  3. Set Build Command to cd ../.. && npm ci && npm run build
  4. Set Start Command to node dist/server/entry.mjs
  5. Add environment variables in the Variables tab

Note

Vercel works best with the @astrojs/vercel adapter instead of @astrojs/node. You would need to swap the adapter in astro.config.mjs.

  1. Import your GitHub repository
  2. Set the Root Directory to apps/hub
  3. Override the Build Command: cd ../.. && npm ci && npm run build
  4. Add environment variables in project settings

Page Rendering

The Hub uses a hybrid rendering strategy:

Page Rendering Notes
/ (Landing) Pre-rendered (static) export const prerender = true
/coming-soon Pre-rendered (static) export const prerender = true
/sign-in, /sign-up, /reset-password Server-rendered Auth pages
/dashboard, /apps, /profile, etc. Server-rendered Protected pages
/api/* Server-rendered API routes

Pre-rendered pages are generated at build time as static HTML. Server-rendered pages are generated on each request by the Node.js server.

Firebase Setup

1. Create a Firebase Project

  1. Go to the Firebase Console
  2. Click Add project
  3. Enter a project name and follow the setup wizard
  4. When prompted, you can optionally enable Google Analytics

2. Register a Web App

  1. In the Firebase Console, click the gear icon > Project settings
  2. Under Your apps, click the web icon (</>)
  3. Register the app with a nickname (e.g., "CyberEco Hub")
  4. Copy the firebaseConfig values to your environment variables

3. Enable Authentication

  1. Go to Authentication > Sign-in method
  2. Enable Email/Password provider
  3. Optionally enable additional providers (Google, GitHub, etc.)

4. Create Firestore Database

  1. Go to Firestore Database > Create database
  2. Choose a location (e.g., nam5 for multi-region US)
  3. Start in production mode (locked down)

5. Deploy Security Rules

Create security rules that match your permission model:

firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Users can read and write their own document
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }

    // Authenticated users can read/write tasks they own
    match /tasks/{taskId} {
      allow read, write: if request.auth != null
        && resource.data.assignee == request.auth.uid;
      allow create: if request.auth != null;
    }

    // Permission logs (write-only for system, read for the user)
    match /permission_logs/{logId} {
      allow read: if request.auth != null
        && resource.data.userId == request.auth.uid;
      allow write: if request.auth != null;
    }

    // Resource permissions
    match /resource_permissions/{permId} {
      allow read: if request.auth != null
        && resource.data.userId == request.auth.uid;
      allow write: if request.auth != null;
    }

    // Default: deny all
    match /{document=**} {
      allow read, write: if false;
    }
  }
}

Security rules are your last line of defense

Even though the DataLayerService checks permissions in application code, Firestore security rules provide server-side enforcement. A malicious client could bypass your application code and call Firestore directly. Always deploy security rules that match your permission model.

Deploy the rules:

npx firebase deploy --only firestore:rules

Publishing Packages

CyberEco uses Changesets for versioning and publishing to GitHub Packages.

Creating a Changeset

When you make changes to packages, create a changeset to describe the change:

npm run changeset

This interactive prompt asks:

  1. Which packages changed? Select the affected packages
  2. What type of change? major, minor, or patch
  3. Summary: A brief description of the change

The changeset is saved as a markdown file in .changeset/.

Fixed versioning

All 4 CyberEco packages use fixed versioning -- they always share the same version number. A changeset for any package bumps all packages.

Versioning

Apply pending changesets to bump versions:

npm run version-packages

This updates all package.json files and generates changelog entries.

Publishing

Build and publish all packages:

npm run publish-packages

This runs turbo build followed by changeset publish.

Automated Publishing (CI/CD)

The repository includes a GitHub Actions workflow that automatically publishes packages:

.github/workflows/publish.yml
name: Publish Packages

on:
  push:
    branches: [main]
    paths:
      - '.changeset/**'
      - 'packages/*/package.json'

jobs:
  publish:
    runs-on: ubuntu-latest
    if: contains(github.event.head_commit.message, 'Version Packages')
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
          registry-url: 'https://npm.pkg.github.com'
          scope: '@cyber-eco'

      - run: npm ci
      - run: npm run build
      - run: npm run test
      - run: npx changeset publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The workflow triggers when a "Version Packages" commit is pushed to main (created by npm run version-packages).

CI/CD Pipeline

Continuous Integration

Every push and pull request to main triggers the CI workflow:

.github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - run: npm ci
      - run: npm run build
      - run: npm run type-check
      - run: npm run test

      # Verify architectural constraint
      - name: Verify no firebase in services
        run: |
          if grep -r "firebase" packages/services/src/ --include="*.ts"; then
            echo "ERROR: packages/services must not import firebase"
            exit 1
          fi

CI Checks

Check Purpose
npm run build All 5 packages build successfully
npm run type-check TypeScript strict mode passes
npm run test All test suites pass
grep firebase services/ Verify @cyber-eco/services has zero Firebase imports

The build is tested against Node.js 18 and 20 to ensure compatibility.

main                  Production branch, protected
  |
  +-- feature/xxx     Feature branches, PR to main
  +-- fix/xxx         Bug fix branches, PR to main
  +-- docs/xxx        Documentation branches, PR to main
  1. Create a feature branch from main
  2. Make changes and create changesets for package changes
  3. Open a pull request -- CI runs automatically
  4. After merge, run npm run version-packages to bump versions
  5. Push the version bump commit -- publish workflow runs automatically

Monitoring

Application Logs

The Hub app logs to stdout. In production, pipe logs to your logging service:

node dist/server/entry.mjs 2>&1 | your-log-collector

Firebase Monitoring

Use the Firebase Console for:

  • Authentication: Monitor sign-ups, sign-ins, and failures
  • Firestore: Monitor read/write operations and usage
  • Performance: Track API latency and errors

Health Check

The Hub app can be monitored with a simple HTTP check:

curl https://hub.cybereco.app/
# Should return 200

Next Steps