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_SECRETis a strong, random string (at least 32 characters) -
PUBLIC_HUB_URLpoints 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:
# 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¶
Turborepo builds all 5 packages in topological dependency order:
@cyber-eco/types(zero deps)@cyber-eco/firebase+@cyber-eco/auth(in parallel, depend on types)@cyber-eco/services(depends on types + auth)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.
The server listens on the port specified by the PORT environment variable (default: 4321).
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:
- Connect your GitHub repository
- Set the Root Directory to
apps/hub - Set Build Command to
cd ../.. && npm ci && npm run build - Set Start Command to
node dist/server/entry.mjs - 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.
- Import your GitHub repository
- Set the Root Directory to
apps/hub - Override the Build Command:
cd ../.. && npm ci && npm run build - 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¶
- Go to the Firebase Console
- Click Add project
- Enter a project name and follow the setup wizard
- When prompted, you can optionally enable Google Analytics
2. Register a Web App¶
- In the Firebase Console, click the gear icon > Project settings
- Under Your apps, click the web icon (
</>) - Register the app with a nickname (e.g., "CyberEco Hub")
- Copy the
firebaseConfigvalues to your environment variables
3. Enable Authentication¶
- Go to Authentication > Sign-in method
- Enable Email/Password provider
- Optionally enable additional providers (Google, GitHub, etc.)
4. Create Firestore Database¶
- Go to Firestore Database > Create database
- Choose a location (e.g.,
nam5for multi-region US) - Start in production mode (locked down)
5. Deploy Security Rules¶
Create security rules that match your permission model:
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:
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:
This interactive prompt asks:
- Which packages changed? Select the affected packages
- What type of change?
major,minor, orpatch - 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:
This updates all package.json files and generates changelog entries.
Publishing¶
Build and publish all packages:
This runs turbo build followed by changeset publish.
Automated Publishing (CI/CD)¶
The repository includes a GitHub Actions workflow that automatically publishes packages:
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:
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.
Recommended Branch Strategy¶
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
- Create a feature branch from
main - Make changes and create changesets for package changes
- Open a pull request -- CI runs automatically
- After merge, run
npm run version-packagesto bump versions - 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:
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:
Next Steps¶
- Installation -- Set up local development
- Testing Guide -- Ensure quality before deploying
- Permissions Guide -- Configure security rules and permissions
- Architecture -- Understand the full system design