stout is designed for enterprise environments with features for private hosting, CI/CD integration, air-gapped installations, and compliance requirements.
- Use Cases
- Private Index Hosting
- Custom Signing Keys
- CI/CD Integration
- Air-Gapped Installations
- Multi-Prefix Environments
- Audit Logging
- Compliance
- Performance at Scale
- 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
- 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
- Audit trails: Track every package installation
- Approved packages: Curate allowed packages via private index
- Vulnerability management: Automated scanning with
stout audit
- Offline operation: Full functionality without internet access
- Controlled updates: Manual mirror synchronization
- Security compliance: No external network dependencies
Host your own stout index for complete control over available packages.
The simplest approach for teams already using GitHub:
# Fork the stout-index repository to your organization
# Make it private
# Update stout configuration~/.stout/config.toml:
[index]
base_url = "https://raw.githubusercontent.com/YOUR_ORG/stout-index/main"For authentication, set a GitHub token:
export STOUT_GITHUB_TOKEN="ghp_xxxxx"Host the index on any web server (nginx, Apache, S3, etc.):
# 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:
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;
}For AWS S3:
# 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:
[index]
base_url = "https://stout.company.cloudfront.net"Create a custom index with only approved packages:
#!/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)Generate and use your own signing keys for complete trust chain control.
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...Option 1: Replace the default key (requires building from source)
Edit crates/stout-index/src/signature.rs:
pub const DEFAULT_PUBLIC_KEY_HEX: &str = "YOUR_PUBLIC_KEY_HEX";Option 2: Add as additional trusted key (runtime configuration)
~/.stout/config.toml:
[security]
additional_trusted_keys = [
"YOUR_PUBLIC_KEY_HEX"
]# 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- Store private keys securely: Use HashiCorp Vault, AWS Secrets Manager, or similar
- Rotate keys periodically: Add new keys to
additional_trusted_keysbefore rotating - Backup keys: Losing the private key requires re-signing all indexes
- Audit key usage: Log all signing operations
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 buildstages:
- 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 buildpipeline {
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'
}
}
}Create a Brewfile for reproducible dependencies:
# 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"Generate and commit lock files for reproducibility:
# 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 verifyBrewfile.lock format:
{
"generated_at": "2024-11-27T12:00:00Z",
"stout_version": "0.1.0",
"packages": {
"jq": {
"version": "1.7.1",
"bottle_sha256": "abc123...",
"dependencies": ["oniguruma"]
}
}
}For environments without internet access.
# 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# 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/~/.stout/config.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# Start HTTP server
stout mirror serve /opt/stout-mirror --port 8080 --bind 0.0.0.0
# Or use nginx/Apache for production# 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/Mirrors support two security models:
Option 1: Preserve Upstream Signatures (Recommended)
The mirror preserves the original official signature:
# 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:
# 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 |
Isolate dependencies for different projects or teams.
# 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"# 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 scipyDockerfile:
FROM ubuntu:22.04
# Install stout
RUN curl -fsSL https://raw.githubusercontent.com/neul-labs/stout/main/install.sh | bash
# 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"]Track package operations for compliance and debugging.
~/.stout/config.toml:
[audit]
enabled = true
log_file = "/var/log/stout/audit.log"
log_format = "json" # or "text"
include_user = true
include_timestamp = trueJSON format:
{
"timestamp": "2024-11-27T12:00:00Z",
"user": "developer",
"action": "install",
"packages": ["jq@1.7.1"],
"success": true,
"duration_ms": 1234,
"source": "bottle"
}Send logs to your SIEM or log aggregator:
# 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 collectionstout supports SOC 2 compliance through:
- Access controls: Package installation requires appropriate permissions
- Audit trails: All operations logged with timestamps and users
- Change management: Version control via lock files
- Vulnerability management: Built-in
stout auditcommand
For HIPAA-regulated environments:
- Air-gapped operation: No external network access required
- Encrypted storage: Compatible with encrypted filesystems
- Access logging: Track who installed what and when
Federal deployments can use:
- Private hosting: No dependency on external services
- Custom signing: Own cryptographic keys
- FIPS compliance: Uses standard cryptographic algorithms
Shared Cache for Build Agents:
# NFS mount
mount -t nfs cache-server:/stout-cache /opt/stout-cache
# Configure stout
export STOUT_CACHE_DIR=/opt/stout-cacheRedis Cache for Metadata:
[cache]
backend = "redis"
redis_url = "redis://cache-server:6379/0"# Increase parallel downloads
stout config set install.parallel_downloads 8
# Parallel builds (source installation)
stout install -s --jobs=16 large-packageExport metrics for monitoring:
# 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| Variable | Description | Default |
|---|---|---|
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 |
# 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 pkgFor additional enterprise support, contact the maintainers or open a discussion on GitHub.