Infrastructure management CLI for deploying full-stack applications with plugin-based configuration.
# Install in your project
npm install @factiii/stack
# Initialize configuration (run this first!)
npx factiii init
# This creates:
# - factiii.yml (user-editable config)
# - factiiiAuto.yml (auto-detected config)
# - .github/workflows/ (CI/CD workflows)
# Edit factiii.yml to replace EXAMPLE- values
# Then run:
npx factiii scan # Check for issues
npx factiii fix # Auto-fix issues
npx factiii deploy --staging # Deploy to stagingFactiii Stack uses a plugin-based architecture where each plugin:
- Defines its own configuration schema
- Auto-detects project settings
- Validates and fixes issues
- Handles deployment for its domain
factiii.yml - User-Editable Configuration
name: my-app
# Environment configurations
staging:
domain: staging.myapp.com
server: mac # OS type: mac, ubuntu, windows, amazon-linux
server_mode: true # Enable server hardening (default: true)
prod:
domain: myapp.com
server: ubuntu # OS type for production
pipeline: aws # Use AWS pipeline for deployment
config: free-tier # AWS tier: ec2, free-tier, standard, enterprise
access_key_id: AKIAXXXXXXXX
region: us-east-1
prisma:
schema_path: null # Optional override
version: null # Optional override
# Exclude Docker containers from unmanaged container cleanup
container_exclusions:
- factiii_postgres
- legacy_containerfactiiiAuto.yml - Auto-Detected Configuration
# Auto-detected by plugins
factiii_version: 1.0.0
has_prisma: true
has_trpc: true
prisma_schema: prisma/schema.prisma
prisma_version: 5.0.0
ssh_user: ubuntu
dockerfile: Dockerfile
package_manager: pnpm
node_version: 20
pnpm_version: 9
aws_cli_installed: trueScans your project and generates configuration files:
npx factiii init # Initialize Factiii Stack
npx factiii init --force # Regenerate configsWhat it does:
- Detects which plugins are relevant to your project
- Generates
factiii.ymlwith only relevant sections - Generates
factiiiAuto.ymlwith auto-detected values - Creates GitHub Actions workflows
Checks all environments for issues:
npx factiii scan # Scan all (dev, secrets, staging, prod)
npx factiii scan --dev # Scan dev only
npx factiii scan --staging # Scan staging only
npx factiii scan --prod # Scan prod onlyNote: Requires factiii.yml to exist. Run npx factiii init first.
Automatically fixes issues where possible:
npx factiii fix # Fix all environments
npx factiii fix --dev # Fix dev only
npx factiii fix --staging # Fix staging only
npx factiii fix --prod # Fix prod onlyNote: Requires factiii.yml to exist. Run npx factiii init first.
Deploys to environments (runs scan first, aborts on issues):
npx factiii deploy --dev # Start local dev containers
npx factiii deploy --staging # Deploy to staging server
npx factiii deploy --prod # Deploy to production serverNote: Requires factiii.yml to exist. Run npx factiii init first.
Manage secrets via Ansible Vault and deploy them directly to servers:
# List all secrets (SSH keys + environment variables)
npx factiii secrets list
# Set SSH keys (required for deployment)
npx factiii secrets set STAGING_SSH
npx factiii secrets set PROD_SSH
# Set environment variables for each stage
npx factiii secrets set-env DATABASE_URL --staging
npx factiii secrets set-env JWT_SECRET --staging
npx factiii secrets set-env DATABASE_URL --prod
npx factiii secrets set-env JWT_SECRET --prod
# List environment variables
npx factiii secrets list-env --staging
npx factiii secrets list-env --prod
# Deploy secrets to servers via SSH
npx factiii secrets deploy --staging # Deploy to staging server
npx factiii secrets deploy --prod # Deploy to production server
npx factiii secrets deploy --all # Deploy to all servers
# Options
npx factiii secrets deploy --staging --restart # Restart container after deploy
npx factiii secrets deploy --staging --dry-run # Show what would be deployedHow it works:
- Secrets are stored locally in Ansible Vault (encrypted)
- When you run
secrets deploy, Factiii:- Reads the SSH key from the vault
- Connects to the server via SSH
- Writes a
.env.{stage}file with your environment variables
- Your application reads the
.env.{stage}file on startup
Note: Requires factiii.yml with Ansible Vault configured. Run npx factiii init first.
Factiii commands work with four stages: dev, secrets, staging, prod.
npx factiii scan # Scan all reachable stages
npx factiii scan --dev # Scan only dev stage
npx factiii scan --staging # Scan only staging stage
npx factiii fix # Fix all reachable stages
npx factiii fix --staging # Fix only staging stage
npx factiii deploy --staging # Deploy to staging
npx factiii deploy --prod # Deploy to prodThe pipeline plugin decides how to reach each stage:
| Stage | How it's reached |
|---|---|
| dev | Always runs locally |
| secrets | Runs locally (needs Ansible Vault configured) |
| staging | Via workflow → SSH → runs with --staging |
| prod | Via workflow → SSH → runs with --prod |
When your CI/CD workflow SSHs to a server to run commands, you MUST specify the stage:
# In your workflow, after SSH to staging server:
GITHUB_ACTIONS=true npx factiii fix --staging # ✅ Correct
npx factiii fix # ❌ Wrong - will try to run all stagesThis prevents the command from trying to reach stages it can't access from the server.
See STANDARDS.md for full documentation of the stage execution pattern.
Pipelines
factiii- GitHub Actions CI/CD with thin workflowsaws- AWS infrastructure (EC2, ECR, free-tier configs)
Servers (OS Types)
mac- macOS (Homebrew, launchctl)ubuntu- Ubuntu Linux (apt, systemd)windows- Windows Server (Chocolatey) - templateamazon-linux- Amazon Linux 2023 (dnf, systemd)
Frameworks
prisma-trpc- Prisma database + tRPC API
Addons
server-mode- Configure machines as deployment servers (disable sleep, enable SSH, etc.)
Each plugin defines:
class MyPlugin {
static id = 'my-plugin';
static category = 'framework'; // or: pipeline, server, addon
// Schema for factiii.yml (user-editable)
static configSchema = {
my_plugin: {
setting: 'default-value'
}
};
// Schema for factiiiAuto.yml (auto-detected)
static autoConfigSchema = {
has_my_plugin: 'boolean',
my_plugin_version: 'string'
};
// Auto-detect configuration
static async detectConfig(rootDir) {
return {
has_my_plugin: true,
my_plugin_version: '1.0.0'
};
}
// Fixes array - issues this plugin can detect and resolve
static fixes = [
{
id: 'missing-config',
stage: 'dev',
severity: 'critical',
description: 'Configuration missing',
scan: async (config, rootDir) => {
// Return true if problem exists
return !config.my_plugin;
},
fix: async (config, rootDir) => {
// Auto-fix the problem
return true;
},
manualFix: 'Add my_plugin config to factiii.yml'
}
];
// Deploy method
async deploy(config, environment) {
// Handle deployment for this environment
}
}GitHub Actions workflows are intentionally minimal - they just SSH into servers and call the CLI:
# .github/workflows/factiii-staging.yml
- name: Deploy via CLI
run: |
ssh user@host << EOF
cd ~/.factiii/my-app
git pull
GITHUB_ACTIONS=true npx factiii deploy --staging
EOFCRITICAL: Workflows MUST specify the stage flag (--staging or --prod) when running commands on servers.
All deployment logic runs on the server in testable JavaScript, not in workflow bash scripts.
Factiii uses Ansible Vault to store and manage deployment secrets (SSH keys, API keys, etc.).
Add Ansible Vault configuration to factiii.yml:
# Ansible Vault configuration (for secrets)
ansible:
vault_path: group_vars/all/vault.yml # Path to vault file
vault_password_file: ~/.vault_pass # Optional: path to password fileProvide the vault password via one of:
- Password file: Set
ansible.vault_password_fileinfactiii.yml(e.g.~/.vault_pass) - Environment variable:
ANSIBLE_VAULT_PASSWORDorANSIBLE_VAULT_PASSWORD_FILE
Security: Never commit the vault password or decrypted vault file to git.
# List all secrets
npx factiii secrets list
# Set a secret (interactive prompt)
npx factiii secrets set STAGING_SSH
# Set a secret (non-interactive)
npx factiii secrets set STAGING_SSH --value "your-key-here"
# Check if secrets exist
npx factiii secrets check- STAGING_SSH - SSH private key for staging server
- PROD_SSH - SSH private key for production server
- AWS_SECRET_ACCESS_KEY - AWS secret key (if using AWS pipeline)
In GitHub Actions workflows, provide the vault password as a GitHub secret:
- Add
ANSIBLE_VAULT_PASSWORDto your repository secrets - Workflows automatically load SSH keys from Ansible Vault using this password
The workflow step npx factiii secrets write-ssh-keys extracts secrets from the vault and writes SSH keys to ~/.ssh/ for deployment steps.
Plugins declare required environment variables:
class MyPlugin {
static requiredEnvVars = ['DATABASE_URL', 'API_KEY'];
}These are automatically validated against:
.env.example(template, committed to git).env(local dev, gitignored, auto-created from example).env.staging(staging values, user creates).env.prod(production values, user creates)
The AWS plugin supports multiple configuration bundles:
# factiii.yml
aws:
config: free-tier # Choose your bundle
region: us-east-1Available Bundles:
ec2- Basic EC2 instancefree-tier- Complete free tier (EC2 + RDS + S3 + ECR)standard- Production-ready setup (coming soon)enterprise- HA, multi-AZ, auto-scaling (coming soon)
Install external plugins via npm:
npm install @factiii/stack-plugin-nextjsFactiii automatically loads plugins from node_modules that match:
@factiii/stack-plugin-*- Listed in
factiii.ymlunderplugins
See STANDARDS.md for plugin development guide.
MIT