This document describes the security model, threat assumptions, and operational guidance for Axiom. It is intended for system administrators, security auditors, and contributors.
- Threat Model
- Trust Anchors
- Build Provenance
- Privilege Requirements
- Key Management
- Secure Operations
- Resource Limits
- Hardening Recommendations
Axiom considers the following adversary classes:
| Adversary | Description | Assumed Capabilities |
|---|---|---|
| Untrusted Manifest | Malicious package manifest from external source | Arbitrary YAML content, crafted constraints |
| Untrusted Binary Cache | Compromised or malicious binary cache server | Arbitrary tarball content, modified signatures |
| Compromised Local User | Non-root user attempting privilege escalation | Local filesystem access, can run axiom CLI |
| Network Attacker | Man-in-the-middle or DNS spoofing | Can intercept HTTP traffic, serve malicious content |
The following are explicitly not protected against:
- Compromised root on the same machine (if root is compromised, all bets are off)
- Physical access to the machine
- Kernel exploits that bypass userspace protections
- Supply chain attacks on the build host before signing (mitigated by provenance tracking)
┌─────────────────────────────────────────────────────────────────┐
│ TRUSTED DOMAIN │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ PGSD Signing │ │ Local Root │ │ Verified Store │ │
│ │ Key (offline)│ │ Authority │ │ (ZFS datasets) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ Signature verification
▼
┌─────────────────────────────────────────────────────────────────┐
│ UNTRUSTED DOMAIN │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Binary Cache │ │ User Input │ │ Network Resources │ │
│ │ Servers │ │ (manifests) │ │ (ports tree, etc.) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
The Pacific Grove Software Distribution Foundation maintains an offline signing key used to sign official packages and bootstrap tarballs.
Trust Chain:
- Root Key (offline, air-gapped) - Signs release keys
- Release Key (per-release) - Signs packages for a specific release
- Package Signature - Ed25519 signature over manifest + content hash
Key Location:
- Public keys:
/axiom/keys/trusted/ - User keys:
~/.axiom/keys/
The local root user is trusted to:
- Configure the Axiom store
- Add trusted signing keys
- Import packages into the store
- Create and manage profiles
When building from ports:
- The FreeBSD ports tree is fetched over HTTPS
- Build outputs are signed with a local key
- Provenance metadata records build environment
WARNING: Building from ports trusts the ports tree content. For maximum security, verify port checksums against known-good values or use pre-signed binary packages.
Build provenance provides cryptographic evidence of how a package was built, enabling verification of the entire supply chain from source to binary.
Every package in the Axiom store should have a provenance.yaml file containing:
format_version: "1.0"
builder:
name: "axiom-builder" # Builder software
version: "1.0.0" # Builder version
host: "builder01.pgsdf.org" # Build host
source:
url: "https://ftp.gnu.org/gnu/bash/bash-5.2.tar.gz"
sha256: "abc123..." # Source archive hash
fetched_at: "2025-01-15T10:00:00Z"
build:
started_at: "2025-01-15T10:05:00Z"
completed_at: "2025-01-15T10:15:00Z"
environment:
PATH: "/axiom/env/build/bin:/usr/bin"
CC: "gcc"
commands:
- "./configure --prefix=/usr/local"
- "make -j8"
- "make install DESTDIR=$OUTPUT"
output:
hash: "sha256:def456..." # Hash of package contents
files_count: 142
total_size: 5242880
signature:
key_id: "PGSD0001A7E3F9B2"
algorithm: "ed25519"
value: "base64..."Provenance uses hash chain binding to cryptographically link the output with build metadata:
output_hash = sha256(package_contents)
provenance_hash = sha256(provenance_yaml_without_signature)
binding = sign(output_hash || provenance_hash, private_key)
This ensures:
- The package contents match the recorded output hash
- The provenance metadata is unmodified
- Both are signed by a trusted key
Configure provenance policy in /etc/axiom/policy.yaml:
provenance:
require: true # Reject packages without provenance
require_signature: true # Reject unsigned provenance
trusted_builders:
- "builder01.pgsdf.org"
- "builder02.pgsdf.org"
max_age_days: 365 # Reject old builds# Verify package provenance
axiom verify-provenance bash
# Show detailed provenance
axiom provenance-show bash
# Check all packages for policy compliance
axiom provenance-policy --check- Trusted Builders: Only accept packages built by trusted infrastructure
- Build Age Limits: Reject old builds that may use outdated dependencies
- Reproducibility: Future support for rebuild verification
- Audit Trail: Provenance records provide forensic evidence
These commands must be run as root:
| Command | Reason |
|---|---|
axiom setup |
Creates ZFS datasets |
axiom ports-import |
Writes to package store |
axiom import |
Writes to package store |
axiom realize |
Creates environment datasets |
axiom gc |
Destroys unused datasets |
axiom key-add |
Modifies trusted key store |
axiom key-remove |
Modifies trusted key store |
axiom bootstrap-import |
Imports packages to store |
These commands can be run as a regular user:
| Command | Notes |
|---|---|
axiom help |
Read-only |
axiom version |
Read-only |
axiom list |
Reads store metadata |
axiom search |
Reads store metadata |
axiom show |
Reads package manifest |
axiom profile-show |
Reads profile YAML |
axiom resolve --dry-run |
No filesystem writes |
axiom verify |
Signature verification |
axiom key-generate |
Creates local key (user directory) |
| Command | Reason |
|---|---|
axiom shell (untrusted env) |
Could execute malicious binaries |
source /axiom/env/*/activate |
Modifies PATH, could run untrusted code |
Best Practice: Activate environments in a non-root shell, then use sudo for specific privileged operations if needed.
- Ed25519 Signing Keys - Used for package signatures
- Trust Store Keys - Public keys trusted for verification
# Generate a new signing keypair (as user)
axiom key-generate --output mykey
# This creates:
# mykey.priv - Private key (KEEP SECRET)
# mykey.pub - Public key (can be shared)# Add a public key to the trust store (as root)
sudo axiom key-add /path/to/key.pub
# List trusted keys
axiom key list
# Remove a trusted key (as root)
sudo axiom key-remove <key-id>| Key Type | Location | Permissions | Backup |
|---|---|---|---|
| Private signing key | Offline/HSM | 600, owner only | Encrypted, offline |
| Public trust store | /axiom/keys/trusted/ |
644 | With system backup |
| User private keys | ~/.axiom/keys/ |
600 | User responsibility |
- Generate new keypair
- Sign new packages with new key
- Add new public key to trust store
- Announce deprecation of old key
- After transition period, remove old key from trust store
When importing packages, Axiom performs:
-
Path Validation - Rejects paths containing:
..(directory traversal)- Absolute paths (would overwrite system files)
- Null bytes
- Symlinks pointing outside package
-
Signature Verification (if signed):
- Verifies Ed25519 signature over manifest
- Checks content hash matches signed hash
- Rejects packages with invalid/missing signatures (when required)
-
Atomic Operations:
- Uses ZFS transactions where possible
- Writes to temporary location, then renames
- Rollback on failure
The resolver is designed to handle untrusted manifests safely:
Protected Against:
- Exponential blowup (resource limits)
- Infinite loops (cycle detection)
- Memory exhaustion (memory limits)
- CPU exhaustion (time limits)
Resource Limit Presets:
# Default limits (balanced)
axiom resolve myprofile
# Strict limits for untrusted manifests
axiom resolve myprofile --strict
# Custom limits
axiom resolve myprofile --timeout 10000 --max-memory 67108864 --max-depth 50When fetching from binary caches:
- HTTPS Required - All cache connections use TLS
- Signature Verification - Packages must be signed
- Hash Verification - Content hash verified after download
- No Arbitrary Code Execution - Downloaded content is never executed during fetch
Configuration:
# /etc/axiom/cache.yaml
caches:
- url: https://cache.pgsd.org
priority: 100
trust: pgsd-release-key
- url: https://my-private-cache.example.com
priority: 50
trust: my-signing-keyAxiom provides a centralized input validation module (src/validation.zig) for secure handling of external input:
URL Validation:
const validation = @import("validation.zig");
const result = validation.UrlValidator.validate(url);
if (!result.valid) {
log.err("Invalid URL: {s}", .{result.error_message});
return error.InvalidUrl;
}
// Use validated components: result.host, result.path, result.portSecurity Checks:
- Rejects path traversal (
..,%2e%2e) - Rejects null bytes and control characters
- Enforces scheme allowlist (http/https only)
- Validates port ranges
JSON/YAML Escaping:
// Safe JSON output
var buf: [1024]u8 = undefined;
const escaped = validation.escapeJsonString(untrusted_input, &buf);
// Check if YAML needs quoting
if (validation.yamlNeedsQuoting(value)) {
const safe = validation.escapeYamlString(value, allocator);
}Numeric Bounds Checking:
// Parse with bounds to prevent overflow
const size = try validation.parseSize(input, 100 * TB); // max 100TB
const timestamp = try validation.parseTimestamp(input, year_2100);
const port = try validation.parseInt(u16, input, 1, 65535);| Limit | Default | Strict | Purpose |
|---|---|---|---|
max_resolution_time_ms |
30,000 | 10,000 | Prevent CPU exhaustion |
max_memory_bytes |
256 MB | 64 MB | Prevent memory exhaustion |
max_dependency_depth |
100 | 50 | Prevent stack overflow |
max_candidates_per_package |
1,000 | 100 | Limit version explosion |
max_total_candidates |
100,000 | 10,000 | Limit total work |
max_sat_variables |
100,000 | 10,000 | Limit SAT complexity |
max_sat_clauses |
1,000,000 | 100,000 | Limit SAT complexity |
Use --strict when:
- Resolving profiles from untrusted sources
- Processing manifests from third-party repositories
- Running in automated pipelines with untrusted input
- Operating in resource-constrained environments
| Limit | Value | Purpose |
|---|---|---|
| Max manifest size | 1 MB | Prevent YAML bomb |
| Max package size | 10 GB | Prevent disk exhaustion |
| Max files per package | 100,000 | Prevent inode exhaustion |
| Max path length | 1024 | Prevent buffer overflow |
-
Separate Build and Runtime
Build Host Runtime Host ┌──────────┐ ┌──────────┐ │ ports │ ZFS │ packages │ │ build │ ────► │ only │ │ signing │ send │ verify │ └──────────┘ └──────────┘ -
Read-Only Store
- After populating the store, set
readonly=on - Use ZFS send/receive for updates
- After populating the store, set
-
Network Isolation
- Build hosts should have minimal network access
- Runtime hosts don't need network for package operations
-
Audit Logging
- Enable ZFS dataset auditing
- Log all
axiomcommands with--audit
-
Offline Signing
- Keep private keys on air-gapped machine
- Sign packages, transfer signatures only
-
Hardware Security Module (HSM)
- For high-security deployments, use HSM for signing
- PKCS#11 integration planned
-
Multi-Party Signing
- Require multiple signatures for critical packages
- Implement threshold signing (planned)
-
Encryption
# Create encrypted store zfs create -o encryption=aes-256-gcm \ -o keyformat=passphrase \ zroot/axiom -
Snapshot Protection
# Prevent snapshot deletion zfs hold axiom:production zroot/axiom/store@release -
Quota Limits
# Prevent store from filling disk zfs set quota=100G zroot/axiom/store
If you discover a security vulnerability in Axiom:
- Do NOT open a public issue
- Email: security@pgsd.org
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
We aim to respond within 48 hours and coordinate disclosure responsibly.
| Date | Version | Change |
|---|---|---|
| 2025-01 | 0.1.0 | Initial security model |
| 2025-01 | 0.1.0 | Phase 24: Secure tar extraction |
| 2025-01 | 0.1.0 | Phase 25: Mandatory signature verification |
| 2025-01 | 0.1.0 | Phase 26: ZFS path validation |
| 2025-01 | 0.1.0 | Phase 27: Build sandboxing |
| 2025-01 | 0.1.0 | Phase 28: Secure bundle verification |
| 2025-01 | 0.1.0 | Phase 29: Resource limits |
| 2025-01 | 0.1.0 | Phase 30: Thread-safe operations |
Author: Vester "Vic" Thacker Organization: Pacific Grove Software Distribution Foundation License: BSD 2-Clause