# Enterprise Deployment Guide
stout is designed for enterprise environments with features for private hosting, CI/CD integration, air-gapped installations, and compliance requirements.
## Table of Contents
- [Use Cases](#use-cases)
- [Private Index Hosting](#private-index-hosting)
- [Custom Signing Keys](#custom-signing-keys)
- [CI/CD Integration](#cicd-integration)
- [Air-Gapped Installations](#air-gapped-installations)
- [Multi-Prefix Environments](#multi-prefix-environments)
- [Audit Logging](#audit-logging)
- [Compliance](#compliance)
- [Performance at Scale](#performance-at-scale)
## Use Cases
### Internal Development Teams
- **Standardized tooling**: Ensure all developers have the same package versions
- **Reproducible builds**: Lock files guarantee consistent environments
- **Fast onboarding**: New developers get productive in minutes
### CI/CD Pipelines
- **Cached dependencies**: Offline mirrors eliminate network latency
- **Deterministic builds**: Pinned versions prevent "works on my machine"
- **Parallel execution**: Multiple jobs share the same package cache
### Regulated Industries
- **Audit trails**: Track every package installation
- **Approved packages**: Curate allowed packages via private index
- **Vulnerability management**: Automated scanning with `stout audit`
### Air-Gapped Environments
- **Offline operation**: Full functionality without internet access
- **Controlled updates**: Manual mirror synchronization
- **Security compliance**: No external network dependencies
## Private Index Hosting
Host your own stout index for complete control over available packages.
### Option 1: GitHub (Private Repository)
The simplest approach for teams already using GitHub:
```bash
# Fork the stout-index repository to your organization
# Make it private
# Update stout configuration
```
**~/.stout/config.toml:**
```toml
[index]
base_url = "https://raw.githubusercontent.com/YOUR_ORG/stout-index/main"
```
For authentication, set a GitHub token:
```bash
export STOUT_GITHUB_TOKEN="ghp_xxxxx"
```
### Option 2: Static File Server
Host the index on any web server (nginx, Apache, S3, etc.):
```bash
# Sync the index locally
cd /path/to/index
./scripts/sync_all.sh
# Structure:
# /path/to/index/
# ├── manifest.json
# ├── index.db.zst
# ├── formulas/
# │ ├── manifest.json
# │ ├── index.db.zst
# │ └── data/
# │ ├── a/
# │ │ └── ack.json.zst
# │ └── ...
# └── casks/
# └── ...
```
**nginx configuration:**
```nginx
server {
listen 443 ssl;
server_name stout.internal.company.com;
ssl_certificate /etc/ssl/stout.crt;
ssl_certificate_key /etc/ssl/stout.key;
root /var/www/stout-index;
location / {
autoindex off;
add_header Cache-Control "public, max-age=300";
}
# Enable gzip for JSON files
gzip on;
gzip_types application/json;
}
```
### Option 3: S3 / Cloud Storage
For AWS S3:
```bash
# Sync index to S3
aws s3 sync /path/to/index s3://company-stout-index/ \
--content-encoding zstd \
--cache-control "max-age=300"
# Configure CloudFront for HTTPS
```
**~/.stout/config.toml:**
```toml
[index]
base_url = "https://stout.company.cloudfront.net"
```
### Curating Packages
Create a custom index with only approved packages:
```python
#!/usr/bin/env python3
"""Sync only approved formulas to private index."""
import json
from pathlib import Path
APPROVED_FORMULAS = [
"git", "curl", "jq", "yq", "python@3.11", "node@20",
"go", "rust", "cmake", "make", "gcc",
# Add your approved packages
]
def filter_index(source_dir: Path, dest_dir: Path):
"""Copy only approved formulas to destination."""
for formula in APPROVED_FORMULAS:
first_char = formula[0]
src = source_dir / "formulas" / "data" / first_char / f"{formula}.json.zst"
if src.exists():
dst = dest_dir / "formulas" / "data" / first_char / f"{formula}.json.zst"
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(src, dst)
# Rebuild the SQLite index with only approved formulas
# (Use the sync scripts with a filter)
```
## Custom Signing Keys
Generate and use your own signing keys for complete trust chain control.
### Generate a Keypair
```bash
cd /path/to/stout-index/scripts
# Install dependencies
uv sync
# Generate new keypair
uv run python sign_index.py generate --output ../keys
# Output:
# Generated keypair:
# Private key: ../keys/stout-index.key
# Public key: ../keys/stout-index.pub
#
# Public key (hex): abc123...
```
### Configure stout to Trust Your Key
**Option 1: Replace the default key** (requires building from source)
Edit `crates/stout-index/src/signature.rs`:
```rust
pub const DEFAULT_PUBLIC_KEY_HEX: &str = "YOUR_PUBLIC_KEY_HEX";
```
**Option 2: Add as additional trusted key** (runtime configuration)
**~/.stout/config.toml:**
```toml
[security]
additional_trusted_keys = [
"YOUR_PUBLIC_KEY_HEX"
]
```
### Sign Your Index
```bash
# Using key file
uv run python sign_index.py sign \
--key ./keys/stout-index.key \
--index-dir /path/to/index
# Using environment variable (for CI)
export STOUT_SIGNING_KEY="private_key_hex"
uv run python sign_index.py sign \
--key '$STOUT_SIGNING_KEY' \
--index-dir /path/to/index
```
### Key Management Best Practices
1. **Store private keys securely**: Use HashiCorp Vault, AWS Secrets Manager, or similar
2. **Rotate keys periodically**: Add new keys to `additional_trusted_keys` before rotating
3. **Backup keys**: Losing the private key requires re-signing all indexes
4. **Audit key usage**: Log all signing operations
## CI/CD Integration
### GitHub Actions
```yaml
name: Build with stout
on: [push, pull_request]
jobs:
build:
runs-on: macos-latest # or ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install stout
run: |
curl -fsSL https://raw.githubusercontent.com/neul-labs/stout/main/install.sh | bash
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Cache stout packages
uses: actions/cache@v4
with:
path: |
~/.stout/downloads
~/.stout/cache
key: stout-${{ runner.os }}-${{ hashFiles('Brewfile.lock') }}
restore-keys: |
stout-${{ runner.os }}-
- name: Install dependencies
run: |
stout update
stout bundle install
- name: Build
run: make build
```
### GitLab CI
```yaml
stages:
- setup
- build
variables:
STOUT_CACHE_DIR: ${CI_PROJECT_DIR}/.stout-cache
install-deps:
stage: setup
image: ubuntu:22.04
cache:
key: stout-${CI_COMMIT_REF_SLUG}
paths:
- .stout-cache/
script:
- curl -fsSL https://raw.githubusercontent.com/neul-labs/stout/main/install.sh | bash
- export PATH="$HOME/.local/bin:$PATH"
- stout update
- stout bundle install
artifacts:
paths:
- /opt/homebrew/Cellar/
build:
stage: build
needs: [install-deps]
script:
- make build
```
### Jenkins
```groovy
pipeline {
agent any
environment {
STOUT_CACHE = "${WORKSPACE}/.stout-cache"
}
stages {
stage('Setup') {
steps {
sh '''
curl -fsSL https://raw.githubusercontent.com/neul-labs/stout/main/install.sh | bash
export PATH="$HOME/.local/bin:$PATH"
stout update
stout bundle install
'''
}
}
stage('Build') {
steps {
sh 'make build'
}
}
}
post {
always {
// Cache cleanup
sh 'stout cleanup --prune=7'
}
}
}
```
### Brewfile for CI
Create a `Brewfile` for reproducible dependencies:
```ruby
# Brewfile
# Build tools
brew "cmake"
brew "make"
brew "ninja"
# Languages
brew "python@3.11"
brew "node@20"
brew "go"
brew "rust"
# Utilities
brew "jq"
brew "yq"
brew "curl"
brew "git"
# Testing
brew "shellcheck"
brew "hadolint"
```
### Lock Files
Generate and commit lock files for reproducibility:
```bash
# Generate lock file from installed packages
stout lock generate
# Install exact versions from lock file
stout lock install
# Verify lock file matches installed
stout lock verify
```
**Brewfile.lock format:**
```json
{
"generated_at": "2024-11-27T12:00:00Z",
"stout_version": "0.1.0",
"packages": {
"jq": {
"version": "1.7.1",
"bottle_sha256": "abc123...",
"dependencies": ["oniguruma"]
}
}
}
```
## Air-Gapped Installations
For environments without internet access.
### Create an Offline Mirror
```bash
# Create mirror with specific packages
stout mirror create /path/to/mirror jq curl git python@3.11
# Include all dependencies
stout mirror create /path/to/mirror --with-deps jq curl
# Mirror everything in a Brewfile
stout mirror create /path/to/mirror --from-brewfile ./Brewfile
# Include casks
stout mirror create /path/to/mirror --casks firefox visual-studio-code
```
### Transfer to Air-Gapped System
```bash
# Create archive
tar -czvf stout-mirror.tar.gz /path/to/mirror
# Transfer via approved method (USB, secure file transfer, etc.)
# On air-gapped system:
tar -xzvf stout-mirror.tar.gz -C /opt/
```
### Configure for Offline Use
**~/.stout/config.toml:**
```toml
[index]
# Point to local mirror
base_url = "file:///opt/stout-mirror"
[security]
# May need to adjust for offline use
allow_unsigned = true # Or use your own signing key
```
### Serve Mirror on Internal Network
```bash
# Start HTTP server
stout mirror serve /opt/stout-mirror --port 8080 --bind 0.0.0.0
# Or use nginx/Apache for production
```
### Update Mirror
```bash
# On internet-connected system
stout mirror update /path/to/mirror
# Verify integrity
stout mirror verify /path/to/mirror
# Transfer updated files
rsync -av /path/to/mirror/ user@airgapped:/opt/stout-mirror/
```
### Mirror Security Options
Mirrors support two security models:
**Option 1: Preserve Upstream Signatures (Recommended)**
The mirror preserves the original official signature:
```bash
# Create mirror - upstream signature is automatically preserved
stout mirror create /path/to/mirror jq curl git
# The manifest.json will include:
# {
# "upstream_signature": {
# "signature": "original_ed25519_sig...",
# "index_sha256": "original_hash...",
# "signed_at": 1732723200
# }
# }
```
Clients verify against the official stout public key.
**Option 2: Enterprise Re-signing**
Sign the mirror with your own key for full control:
```bash
# Generate enterprise keypair (one-time)
cd /path/to/signing-tools
uv run python sign_index.py generate --output ./keys
# Save keys/stout-index.key securely (e.g., HashiCorp Vault)
# Distribute keys/stout-index.pub to clients
# Sign the mirror
uv run python sign_index.py sign \
--key ./keys/stout-index.key \
--index-dir /path/to/mirror
# Configure clients
# ~/.stout/config.toml
[security]
additional_trusted_keys = ["YOUR_ENTERPRISE_PUBLIC_KEY"]
```
**Comparison:**
| Aspect | Preserve Upstream | Enterprise Re-sign |
|--------|-------------------|-------------------|
| Setup complexity | None | Requires key management |
| Trust chain | Official → Mirror → Client | Enterprise → Client |
| Key rotation | Handled by stout team | Self-managed |
| Custom packages | Not supported | Fully supported |
| Audit trail | Traceable to official | Internal only |
## Multi-Prefix Environments
Isolate dependencies for different projects or teams.
### Project-Specific Prefixes
```bash
# Create isolated environment for project
stout prefix create ~/projects/api-service/.stout
# Install project dependencies
stout --prefix=~/projects/api-service/.stout bundle install
# Activate in shell
export PATH="$HOME/projects/api-service/.stout/bin:$PATH"
export STOUT_PREFIX="$HOME/projects/api-service/.stout"
```
### Team-Shared Prefixes
```bash
# Create shared prefix
sudo stout prefix create /opt/team-data-science
# Set permissions
sudo chown -R :data-science /opt/team-data-science
sudo chmod -R g+w /opt/team-data-science
# Team members use
stout --prefix=/opt/team-data-science install pandas numpy scipy
```
### Container Integration
**Dockerfile:**
```dockerfile
FROM ubuntu:22.04
# Install stout
# Create app-specific prefix
RUN stout prefix create /app/.stout
# Install dependencies
COPY Brewfile /app/
RUN stout --prefix=/app/.stout bundle install
# Add to PATH
ENV PATH="/app/.stout/bin:$PATH"
ENV STOUT_PREFIX="/app/.stout"
COPY . /app
WORKDIR /app
CMD ["./start.sh"]
```
## Audit Logging
Track package operations for compliance and debugging.
### Enable Audit Logging
**~/.stout/config.toml:**
```toml
[audit]
enabled = true
log_file = "/var/log/stout/audit.log"
log_format = "json" # or "text"
include_user = true
include_timestamp = true
```
### Log Format
**JSON format:**
```json
{
"timestamp": "2024-11-27T12:00:00Z",
"user": "developer",
"action": "install",
"packages": ["jq@1.7.1"],
"success": true,
"duration_ms": 1234,
"source": "bottle"
}
```
### Centralized Logging
Send logs to your SIEM or log aggregator:
```bash
# Syslog
logger -t stout "$(cat /var/log/stout/audit.log)"
# Fluent Bit / Fluentd
# Configure to tail /var/log/stout/audit.log
# Datadog
# Use the Datadog agent with log collection
```
## Compliance
### SOC 2
stout supports SOC 2 compliance through:
1. **Access controls**: Package installation requires appropriate permissions
2. **Audit trails**: All operations logged with timestamps and users
3. **Change management**: Version control via lock files
4. **Vulnerability management**: Built-in `stout audit` command
### HIPAA
For HIPAA-regulated environments:
1. **Air-gapped operation**: No external network access required
2. **Encrypted storage**: Compatible with encrypted filesystems
3. **Access logging**: Track who installed what and when
### FedRAMP
Federal deployments can use:
1. **Private hosting**: No dependency on external services
2. **Custom signing**: Own cryptographic keys
3. **FIPS compliance**: Uses standard cryptographic algorithms
## Performance at Scale
### Caching Strategies
**Shared Cache for Build Agents:**
```bash
# NFS mount
mount -t nfs cache-server:/stout-cache /opt/stout-cache
# Configure stout
export STOUT_CACHE_DIR=/opt/stout-cache
```
**Redis Cache for Metadata:**
```toml
[cache]
backend = "redis"
redis_url = "redis://cache-server:6379/0"
```
### Parallel Operations
```bash
# Increase parallel downloads
stout config set install.parallel_downloads 8
# Parallel builds (source installation)
stout install -s --jobs=16 large-package
```
### Monitoring
Export metrics for monitoring:
```bash
# Prometheus metrics endpoint
stout metrics serve --port 9090
# Metrics available:
# - stout_packages_installed
# - stout_cache_hit_ratio
# - stout_download_duration_seconds
# - stout_install_duration_seconds
```
## Quick Reference
### Environment Variables
| `STOUT_PREFIX` | Installation prefix | `/opt/homebrew` |
| `STOUT_CACHE_DIR` | Cache directory | `~/.stout/cache` |
| `STOUT_CONFIG` | Config file path | `~/.stout/config.toml` |
| `STOUT_INDEX_URL` | Index URL override | (from config) |
| `STOUT_GITHUB_TOKEN` | GitHub auth token | (none) |
| `STOUT_SIGNING_KEY` | Signing key (CI) | (none) |
| `STOUT_LOG_LEVEL` | Log verbosity | `info` |
| `STOUT_NO_COLOR` | Disable colors | `false` |
### CLI Flags for Enterprise
```bash
# Use specific prefix
stout --prefix=/custom/path install pkg
# Verbose output for debugging
stout -v install pkg
stout -vv install pkg # More verbose
# Skip signature verification (development only)
stout update --insecure
# Dry run (show what would happen)
stout install --dry-run pkg
# Force operations
stout install --force pkg
```
---
For additional enterprise support, contact the maintainers or open a discussion on GitHub.