A self-hosted, local-first AI development platform with enterprise security.
Run Large Language Models locally with HashiCorp Vault, GPG-signed commits, and rootless Podman.
| Requirement | Value | Notes |
|---|---|---|
| Container Engine | Podman 4.9+ | Rootless mode required |
| GPG | 2.2+ | All commits must be signed |
| CPU | 4 cores | 8+ cores recommended |
| RAM | 8 GB | 16+ GB for larger models |
| Disk | 32 GB | 128+ GB for multiple models |
| OS | Linux | Ubuntu 22.04+ tested |
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y podman gnupg2 git
# Fedora/RHEL
sudo dnf install -y podman gnupg2 git
# Verify installations
podman --version # Should show 4.9+
gpg --version # Should show 2.2+AIXCL requires rootless Podman. Run the setup script once per machine:
./scripts/utils/setup-podman-rootless.shWhat this modifies on your local machine:
| File | Purpose |
|---|---|
~/.bashrc |
Adds DOCKER_BIN=podman and DOCKER_HOST exports |
/etc/subuid |
Configures subordinate UIDs for rootless containers (via sudo) |
/etc/subgid |
Configures subordinate GIDs for rootless containers (via sudo) |
~/.config/containers/containers.conf |
Podman network and security defaults |
~/.config/containers/registries.conf |
Docker Hub search registry |
~/.config/containers/storage.conf |
Rootless storage driver settings |
.env.podman (repo root) |
Project-specific DOCKER_HOST override |
After setup, reload your shell:
source ~/.bashrcVerify rootless mode:
podman info | grep "rootless"
# Should show: "rootless: true"Note: GPG signing is required only for CODEOWNERS pushing directly to main. Contributors using the fork+PR workflow do not need GPG. See CONTRIBUTING.md.
For CODEOWNERS:
# Generate GPG key (if you don't have one)
gpg --full-generate-key
# List your keys
gpg --list-secret-keys --keyid-format LONG
# Configure Git to sign commits
git config --global commit.gpgsign true
git config --global user.signingkey YOUR_KEY_ID
# Export public key for GitHub
gpg --armor --export YOUR_KEY_ID
# Copy output and add to GitHub Settings → SSH and GPG keys# Generate .env file, admin credentials, and Vault secrets (one-time)
./aixcl stack init
# Start with system profile (includes all services)
./aixcl stack start --profile sys
# Wait for healthy status (about 60 seconds)
./aixcl stack statusServices started (12 in sys profile):
| Category | Service | Port |
|---|---|---|
| Runtime | Ollama (inference) | 11434 |
| OpenCode (agent) | Plugin | |
| Persistence | PostgreSQL | 5432 |
| pgAdmin | 5050 | |
| Observability | Prometheus | 9090 |
| Grafana | 3000 | |
| Loki (logs) | 3100 | |
| cAdvisor (containers) | - | |
| node-exporter (host) | 9100 | |
| postgres-exporter (DB) | 9187 | |
| alertmanager | 9093 | |
| nvidia-gpu-exporter | 9445 | |
| Secrets | Vault | 8200 |
| UI | Open WebUI | 8080 |
Vault initializes automatically when the stack starts. No manual steps required.
# Check Vault status
./aixcl vault status
# Or access Vault UI at http://localhost:8200
# Token: aixcl-dev-tokenA. Via Open WebUI (Browser)
# Add a small test model first
./aixcl models add qwen2.5-coder:0.5b- Open http://localhost:8080 in your browser
- Click "Get Started" to create an account (first user becomes admin)
- Click "New Chat" in the top left
- Select "qwen2.5-coder:0.5b" from the model dropdown
- Type: "Hello! Can you confirm you're working?"
- Expected: The model responds with a greeting
B. Via OpenCode CLI
# Add the model
./aixcl models add qwen2.5-coder:0.5b
# Start OpenCode
opencode
# At the prompt, type:
# > Hello! Can you help me write a Python function?
# Expected: The AI responds with code suggestionsC. Via API (curl)
# Add the model
./aixcl models add qwen2.5-coder:0.5b
# Test via API
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5-coder:0.5b",
"messages": [{"role": "user", "content": "Hello, are you working?"}]
}'
# Expected output: JSON response with the model's replyAll three methods should show the model responding to your hello!
| Service | URL | Login |
|---|---|---|
| Open WebUI | http://localhost:8080 | First user = admin |
| pgAdmin | http://localhost:5050 | Vault credentials |
| Grafana | http://localhost:3000 | Vault credentials |
| Vault UI | http://localhost:8200 | Token: aixcl-dev-token |
| Prometheus | http://localhost:9090 | No auth (localhost only) |
| Loki | http://localhost:3100 | No auth (localhost only) |
| Alertmanager | http://localhost:9093 | No auth (localhost only) |
| Ollama API | http://localhost:11434 | No auth (localhost only) |
Get current service credentials:
./aixcl vault credentialsEvery commit must be GPG-signed:
# Configure Git (one-time)
git config --global commit.gpgsign true
# Commits are automatically signed
vim some-file.txt
git add some-file.txt
git commit -m "feat: add new feature" # Automatically signed
# Verify signature
git log --show-signature -1| Task | Command |
|---|---|
| Check status | ./aixcl stack status |
| View logs | ./aixcl stack logs |
| Stop stack | ./aixcl stack stop |
| Add model | ./aixcl models add <model> |
| Chat CLI | opencode |
| Vault credentials | ./aixcl vault credentials |
| Rotate credentials | ./aixcl vault rotate |
| Verify GPG | gpg --list-secret-keys --keyid-format LONG |
The following are not optional and cannot be disabled:
- ✅ Podman rootless - No privileged containers
- ✅ GPG-signed commits - All commits to main must be signed (CODEOWNERS only)
- ✅ HashiCorp Vault - Dynamic secrets with automatic rotation
- ✅ PostgreSQL SSL - Encrypted database connections
- ✅ Host firewall - Network isolation at host level
See SECURITY.md for architecture details.
# Re-run setup
./scripts/utils/setup-gpg.sh
# Verify key exists
gpg --list-secret-keys --keyid-format LONG# Check user namespaces
sysctl kernel.unprivileged_userns_clone
# Should return 1, if not:
echo 'kernel.unprivileged_userns_clone=1' | sudo tee /etc/sysctl.d/99-userns.conf
sudo sysctl --systemIf ./scripts/utils/setup-podman-rootless.sh fails, check manually:
# Verify subordinate UIDs/GIDs are configured
grep "^$(whoami):" /etc/subuid
grep "^$(whoami):" /etc/subgid
# If missing, add them manually (requires sudo)
echo "$(whoami):100000:65536" | sudo tee -a /etc/subuid
echo "$(whoami):100000:65536" | sudo tee -a /etc/subgid
# Start the podman socket manually
systemctl --user start podman.socket
# Re-run the setup script
./scripts/utils/setup-podman-rootless.sh# Check Vault status
podman logs vault | tail -20
# Ensure Vault is healthy before init
./aixcl stack status# Check for port conflicts
sudo lsof -i :11434 # Ollama
sudo lsof -i :8080 # Open WebUI
sudo lsof -i :8200 # Vault
# Clean restart
./aixcl stack stop
./aixcl utils clean
./aixcl stack start --profile sys- Add more models:
./aixcl models add qwen2.5-coder:1.5b - Customize: Edit
.envfor your environment - Learn: See docs/ for detailed guides
- Contribute: Read CONTRIBUTING.md
Apache License 2.0