| name | fnox-secrets |
| description | fnox Secrets Management Skill |
| version | 1.0.0 |
fnox Secrets Management Skill
name: fnox-secrets
description: Secure secrets management with age encryption, root-protected keys, and GF(3) conservation via DuckDB/ACSet catalog
version: 1.0.0
trit: -1 # Validator/constrainer role in GF(3) triadic system
Overview
fnox is a secrets management tool that encrypts secrets with age and stores them in a git-safe fnox.toml. This skill documents the secure setup with root-protected keys and ACSet-aligned DuckDB catalog.
Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ FNOX SECURE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ /var/keys/age/key.txt ─────────┐ (root:wheel 600) │
│ │ │
│ ▼ │
│ fnox --age-key-file ──▶ DECRYPTS ──▶ ~/fnox.toml │
│ (42 age-encrypted secrets) │
│ │
│ ~/.config/keys/catalog.duckdb ◀──── ACSet-aligned catalog │
│ ~/.config/keys/schema.jl ◀───────── Julia ACSet schema │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Installation
# Install fnox via cargo
cargo install fnox
# Install age via flox (or brew)
flox install age
# Generate age keypair
mkdir -p ~/.age
age-keygen -o ~/.age/key.txt
Configuration
Initialize fnox
cd ~
fnox init
fnox provider add myage age
Edit fnox.toml
[providers.myage]
type = "age"
recipients = ["age1your_public_key_here"]
[profiles.default]
# Default profile secrets
[profiles.prod]
# Production secrets
[profiles.staging]
# Staging secrets
Commands
Set a Secret
fnox set SECRET_NAME "secret_value" --provider myage
fnox set DATABASE_URL "postgres://..." --provider myage
fnox set API_KEY "sk-..." --provider myage -p prod # prod profile
Get a Secret
fnox get SECRET_NAME --age-key-file ~/.age/key.txt
# With root-protected key
sudo cat /var/keys/age/key.txt > /tmp/k && fnox get SECRET_NAME --age-key-file /tmp/k && rm /tmp/k
List Secrets
fnox list
fnox list -p prod # prod profile only
Run Command with Secrets as Env Vars
fnox exec --age-key-file ~/.age/key.txt -- ./my-app
fnox exec --age-key-file ~/.age/key.txt -- env | grep APTOS
Import/Export
fnox export --format env > .env.encrypted
fnox import --format env < .env
Profiles
fnox profiles # List profiles
fnox set KEY "val" -p prod # Set in prod profile
fnox get KEY -p prod # Get from prod profile
FNOX_PROFILE=prod fnox list # Use prod by default
Shell Integration
Add to ~/.zshrc:
# fnox secret management (GF(3) integrated)
export FNOX_AGE_KEY_FILE=~/.age/key.txt
eval "$(~/.cargo/bin/fnox activate zsh)"
For root-protected keys, use the wrapper:
# /usr/local/bin/fnox-secure
#!/bin/bash
TEMP_KEY=$(mktemp)
trap "rm -f $TEMP_KEY" EXIT
sudo cat /var/keys/age/key.txt > "$TEMP_KEY"
~/.cargo/bin/fnox --age-key-file "$TEMP_KEY" "$@"
Root-Protected Key Setup
Move Keys to Root Storage
sudo mkdir -p /var/keys/{age,aptos/worlds,aptos/testnet}
sudo cp ~/.age/key.txt /var/keys/age/
sudo cp ~/.aptos/worlds/*.key /var/keys/aptos/worlds/
sudo chown -R root:wheel /var/keys
sudo chmod -R 600 /var/keys
sudo chmod 700 /var/keys /var/keys/age /var/keys/aptos /var/keys/aptos/worlds
Verify
sudo cat /var/keys/age/key.txt > /tmp/k && fnox get TEST_SECRET --age-key-file /tmp/k && rm /tmp/k
DuckDB Catalog
Schema
CREATE TABLE identity (id INTEGER PRIMARY KEY, name VARCHAR, path VARCHAR, trit TINYINT);
CREATE TABLE keypair (id INTEGER PRIMARY KEY, identity_id INTEGER, pubkey VARCHAR, privkey_path VARCHAR, algorithm VARCHAR, trit TINYINT);
CREATE TABLE provider (id INTEGER PRIMARY KEY, name VARCHAR, trit TINYINT);
CREATE TABLE profile (id INTEGER PRIMARY KEY, name VARCHAR, trit TINYINT);
CREATE TABLE secret (id INTEGER PRIMARY KEY, name VARCHAR, keypair_id INTEGER, provider_id INTEGER, profile_id INTEGER, trit TINYINT);
Query Examples
# List all secrets
duckdb ~/.config/keys/catalog.duckdb "SELECT name FROM secret"
# Check GF(3) conservation
duckdb ~/.config/keys/catalog.duckdb "SELECT * FROM gf3_total"
# Find Aptos keys
duckdb ~/.config/keys/catalog.duckdb "SELECT name FROM secret WHERE name LIKE 'APTOS%'"
GF(3) Conservation
All entities are assigned trits (-1, 0, +1) such that:
Σ(trits) ≡ 0 (mod 3)
| Entity | Trit Assignment |
|---|---|
| age identity | -1 (validator) |
| ssh identity | 0 (coordinator) |
| gpg identity | +1 (generator) |
| default profile | 0 |
| prod profile | +1 |
| staging profile | -1 |
| secrets | cyclic: -1, 0, +1, -1, ... |
ACSet Schema (Julia)
@present SchKeyStore(FreeSchema) begin
Identity::Ob
KeyPair::Ob
Secret::Ob
Provider::Ob
Profile::Ob
keypair_identity::Hom(KeyPair, Identity)
secret_keypair::Hom(Secret, KeyPair)
secret_provider::Hom(Secret, Provider)
secret_profile::Hom(Secret, Profile)
TritType::AttrType
identity_trit::Attr(Identity, TritType)
keypair_trit::Attr(KeyPair, TritType)
secret_trit::Attr(Secret, TritType)
end
Current Secrets Inventory
Aptos Keys (38)
| Secret | Description |
|---|---|
APTOS_ALICE_KEY |
Alice world key |
APTOS_BOB_KEY |
Bob world key |
APTOS_WORLD_[A-Z]_KEY |
26 world keys |
APTOS_ALICE_MAINNET_KEY |
Alice mainnet profile |
APTOS_ALICE_TESTNET_KEY |
Alice testnet profile |
APTOS_BOB_MAINNET_KEY |
Bob mainnet profile |
APTOS_BOB_TESTNET_KEY |
Bob testnet profile |
APTOS_DEFAULT_KEY |
Default profile |
APTOS_TESTNET_ACCOUNT_KEY |
Testnet account |
APTOS_TESTNET_CONSENSUS_KEY |
Testnet consensus |
APTOS_TESTNET_FULLNODE_KEY |
Testnet fullnode |
APTOS_TESTNET_VALIDATOR_KEY |
Testnet validator |
APTOS_TESTNET_MINT_KEY |
Testnet mint |
Other Secrets (4)
| Secret | Description |
|---|---|
AMP_API_KEY |
AMP API key |
GOOGLE_CLIENT_SECRET_PATH |
Google OAuth path |
TEST_SECRET |
Test secret |
Integration with Other Skills
cognitive-surrogate (trit: 0)
# Use fnox secrets in surrogate training
fnox exec --age-key-file ~/.age/key.txt -- python train_surrogate.py
acsets (trit: -1)
# Query catalog via ACSet navigation
secrets_for_identity(ks, aptos_identity_id)
gay-mcp (trit: +1)
# Deterministic secret access coloring
seed = GaySeed.from_string("fnox-session")
color = derive_color(seed, secret_name)
Triadic Bundle
fnox-secrets (-1) ⊗ cognitive-surrogate (0) ⊗ gay-mcp (+1) = 0 ✓
Files
~/.cargo/bin/fnox # fnox binary
~/fnox.toml # Encrypted secrets (git-safe)
/var/keys/age/key.txt # Root-protected age key
/var/keys/aptos/ # Root-protected Aptos keys
~/.config/keys/catalog.duckdb # DuckDB catalog
~/.config/keys/schema.jl # ACSet schema
/usr/local/bin/fnox-secure # Sudo wrapper (optional)
Troubleshooting
"No providers configured"
fnox provider add myage age
# Then edit fnox.toml to set recipients
"Cannot decrypt"
# Check age key path
fnox get SECRET --age-key-file ~/.age/key.txt
# For root-protected
sudo cat /var/keys/age/key.txt > /tmp/k && fnox get SECRET --age-key-file /tmp/k; rm /tmp/k
"Secret not found"
fnox list # Check secret exists
fnox list -p prod # Check correct profile