feat: Add flake and ragenix package generation and dev environment

This commit is contained in:
2026-01-30 12:02:13 -05:00
parent 59ad94bba2
commit b77bca6f0a
13 changed files with 1293 additions and 0 deletions

3
.envrc Normal file
View File

@@ -0,0 +1,3 @@
# Automatically load the Nix development shell when entering this directory
# Requires direnv: https://direnv.net/
use flake

4
.gitignore vendored
View File

@@ -35,3 +35,7 @@ management-dashboard-web-app/users.txt
# Jupyter Notebooks
*.ipynb
# Nix
result
result-*
.direnv/

264
FLAKE_SETUP.md Normal file
View File

@@ -0,0 +1,264 @@
# USDA Vision - Nix Flake Setup
This directory now has a Nix flake for building and developing the USDA Vision system, with ragenix for managing secrets.
## Quick Start
### Development Environment
Enter the development shell with all tools:
```bash
cd usda-vision
nix develop
```
This gives you:
- Docker & Docker Compose
- Node.js 20 with npm/pnpm
- Python 3.11 with pip/virtualenv
- Supabase CLI
- Camera SDK (automatically in `LD_LIBRARY_PATH`)
- ragenix for secrets management
- All standard utilities (jq, yq, rsync, etc.)
### Building
Build the package:
```bash
nix build
# Or explicitly:
nix build .#usda-vision
```
Build just the camera SDK:
```bash
nix build .#camera-sdk
```
## Secrets Management with ragenix
### Initial Setup
1. **Generate or use an age key**:
```bash
# Option 1: Generate a new age key
mkdir -p ~/.config/age
age-keygen -o ~/.config/age/keys.txt
# Option 2: Use your SSH key
ssh-to-age < ~/.ssh/id_ed25519.pub
# Copy the output to secrets/secrets.nix
```
2. **Add your public key** to [secrets/secrets.nix](secrets/secrets.nix):
```nix
{
publicKeys = [
"age1your_public_key_here"
# or
"ssh-ed25519 AAAA... user@host"
];
}
```
3. **Create encrypted environment files**:
```bash
nix develop # Enter dev shell first
ragenix -e secrets/env.age
```
This opens your `$EDITOR` to edit the encrypted file. Add your environment variables:
```bash
# Web environment (Vite)
VITE_SUPABASE_URL=http://exp-dash:54321
VITE_SUPABASE_ANON_KEY=your-anon-key-here
# ... etc
```
For Azure OAuth:
```bash
ragenix -e secrets/env.azure.age
```
### Using Secrets in Development
In the development shell, you can:
```bash
# Edit secrets
ragenix -e secrets/env.age
# View decrypted content (careful in shared screens!)
age -d -i ~/.config/age/keys.txt secrets/env.age
# Re-encrypt all secrets after adding a new public key
ragenix -r
```
### Using Secrets in Production (NixOS)
The flake includes a NixOS module that handles secrets automatically:
```nix
# In your NixOS configuration
{
inputs.usda-vision.url = "path:/path/to/usda-vision";
# ... in your module:
imports = [ inputs.usda-vision.nixosModules.default ];
services.usda-vision = {
enable = true;
secretsFile = config.age.secrets.usda-vision-env.path;
};
# Configure ragenix/agenix to decrypt the secrets
age.secrets.usda-vision-env = {
file = inputs.usda-vision + "/secrets/env.age";
mode = "0644";
owner = "root";
};
}
```
## Project Structure
```
usda-vision/
├── flake.nix # Flake definition with outputs
├── package.nix # Main application build
├── camera-sdk.nix # Camera SDK build
├── secrets.nix # ragenix configuration
├── secrets/
│ ├── secrets.nix # Public keys
│ ├── env.age # Encrypted .env (safe to commit)
│ ├── env.azure.age # Encrypted Azure config (safe to commit)
│ └── README.md # Secrets documentation
└── ... (rest of the app)
```
## Migration from Old Setup
### Old Workflow
- Manual `.env` file management
- Secrets in plaintext (git-ignored)
- Build defined in parent `default.nix`
### New Workflow
- Encrypted `.age` files in git
- Secrets managed with ragenix
- Self-contained flake in `usda-vision/`
- Development shell with all tools
### Migration Steps
1. **Encrypt existing `.env` files**:
```bash
cd usda-vision
nix develop
# Setup your age key first (see above)
# Encrypt the main .env
ragenix -e secrets/env.age
# Paste contents of old .env file, save and exit
# Encrypt Azure config
ragenix -e secrets/env.azure.age
# Paste contents of old .env.azure file, save and exit
```
2. **Delete unencrypted files** (they're git-ignored but still local):
```bash
rm .env .env.azure management-dashboard-web-app/.env
```
3. **Commit encrypted secrets**:
```bash
git add secrets/env.age secrets/env.azure.age secrets/secrets.nix
git commit -m "Add encrypted secrets with ragenix"
```
## Benefits
### Security
- ✅ Secrets encrypted at rest
- ✅ Safe to commit to git
- ✅ Key-based access control
- ✅ Audit trail (git history)
### Development
- ✅ Reproducible environment
- ✅ All tools included
- ✅ No manual setup
- ✅ Version-locked dependencies
### Deployment
- ✅ Declarative secrets management
- ✅ Automatic decryption on NixOS
- ✅ No manual key distribution
- ✅ Clean integration with existing infrastructure
## Common Tasks
### Add a new developer
1. They generate an age key or use their SSH key
2. They send you their public key
3. You add it to `secrets/secrets.nix`
4. Re-encrypt all secrets: `ragenix -r`
5. Commit and push
### Rotate a secret
1. Edit the encrypted file: `ragenix -e secrets/env.age`
2. Update the value
3. Save and exit
4. Commit: `git commit secrets/env.age -m "Rotate API key"`
### Build without flakes
If you need to build on a system without flakes enabled:
```bash
nix-build -E '(import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) { src = ./.; }).defaultNix.default'
```
## Troubleshooting
### "error: getting status of '...': No such file or directory"
Make sure you're in the `usda-vision` directory when running `nix develop` or `nix build`.
### "cannot decrypt: no valid identity"
Your age private key isn't found. Check:
- `~/.config/age/keys.txt` exists
- Your public key is in `secrets/secrets.nix`
- You've run `ragenix -r` after adding your key
### "experimental feature 'flakes' not enabled"
Add to `~/.config/nix/nix.conf`:
```
experimental-features = nix-command flakes
```
Or run with: `nix --experimental-features 'nix-command flakes' develop`
## Further Reading
- [Nix Flakes](https://nixos.wiki/wiki/Flakes)
- [ragenix](https://github.com/yaxitech/ragenix)
- [age encryption](https://github.com/FiloSottile/age)

241
SETUP_COMPLETE.md Normal file
View File

@@ -0,0 +1,241 @@
# USDA Vision - Flake Migration Complete ✅
## Summary
Your USDA Vision repository now has:
1. **Self-contained Nix flake** (`flake.nix`)
- Independent build system
- Development environment
- NixOS module for deployment
2. **Encrypted secrets management** (ragenix)
- `.age` files safe to commit to git
- Key-based access control
- No more plaintext `.env` files
3. **Modular build** (package.nix, camera-sdk.nix)
- Cleaner organization
- Easier to maintain
- Reusable components
4. **Updated parent** (../default.nix)
- Now references the flake
- Removed 200+ lines of inline derivations
## Files Added
### Core Flake Files
-`flake.nix` - Main flake definition with outputs
-`package.nix` - Application build logic
-`camera-sdk.nix` - Camera SDK build logic
-`secrets.nix` - ragenix configuration
### Secrets Infrastructure
-`secrets/secrets.nix` - Public key list
-`secrets/README.md` - Secrets documentation
-`secrets/.gitignore` - Protect plaintext files
### Documentation & Helpers
-`FLAKE_SETUP.md` - Complete setup guide
-`setup-dev.sh` - Interactive setup script
-`.envrc` - direnv integration (optional)
### Parent Directory
-`NIX_FLAKE_MIGRATION.md` - Migration summary
## Next Steps
### 1. Commit the Flake Files
The flake needs to be in git to work:
```bash
cd /home/engr-ugaif/usda-dash-config/usda-vision
# Add all new flake files
git add flake.nix package.nix camera-sdk.nix secrets.nix
git add secrets/secrets.nix secrets/README.md secrets/.gitignore
git add FLAKE_SETUP.md setup-dev.sh .envrc .gitignore
# Commit
git commit -m "Add Nix flake with ragenix secrets management
- Self-contained flake build system
- Development shell with all tools
- ragenix for encrypted secrets
- Modular package definitions
"
```
### 2. Set Up Your Age Key
```bash
cd /home/engr-ugaif/usda-dash-config/usda-vision
# Option A: Use the interactive setup script
./setup-dev.sh
# Option B: Manual setup
mkdir -p ~/.config/age
age-keygen -o ~/.config/age/keys.txt
# Then add your public key to secrets/secrets.nix
```
### 3. Encrypt Your Secrets
```bash
# Enter the development environment
nix develop
# Encrypt main .env file
ragenix -e secrets/env.age
# Paste your current .env contents, save, exit
# Encrypt Azure config
ragenix -e secrets/env.azure.age
# Paste your current .env.azure contents, save, exit
# Commit encrypted secrets
git add secrets/env.age secrets/env.azure.age
git commit -m "Add encrypted environment configuration"
```
### 4. Test the Setup
```bash
# Test that the build works
nix build
# Test the development shell
nix develop
# You should see a welcome message
# Inside the dev shell, verify tools
docker-compose --version
supabase --version
ragenix --help
```
### 5. Update the Parent Repository
```bash
cd /home/engr-ugaif/usda-dash-config
# Commit the updated default.nix
git add default.nix NIX_FLAKE_MIGRATION.md
git commit -m "Update default.nix to use usda-vision flake
- Removed inline derivations
- Now references usda-vision flake packages
- Cleaner, more maintainable code
"
```
### 6. Clean Up Old Files (Optional)
After verifying everything works, you can delete the old plaintext secrets:
```bash
cd /home/engr-ugaif/usda-dash-config/usda-vision
# These are already git-ignored, but remove them locally
rm -f .env .env.azure management-dashboard-web-app/.env
echo "✅ Old plaintext secrets removed"
```
## Verification Checklist
- [ ] Flake files committed to git
- [ ] Age key generated at `~/.config/age/keys.txt`
- [ ] Public key added to `secrets/secrets.nix`
- [ ] Secrets encrypted and committed
- [ ] `nix build` succeeds
- [ ] `nix develop` works
- [ ] Parent `default.nix` updated and committed
- [ ] Old `.env` files deleted
## Usage Quick Reference
### Development
```bash
# Enter dev environment (one-time per session)
cd usda-vision
nix develop
# Edit secrets
ragenix -e secrets/env.age
# Normal docker-compose workflow
docker-compose up -d
docker-compose logs -f
```
### Building
```bash
# Build everything
nix build
# Build specific packages
nix build .#usda-vision
nix build .#camera-sdk
```
### Secrets Management
```bash
# Edit encrypted secret
ragenix -e secrets/env.age
# Re-key after adding a new public key
ragenix -r
# View decrypted (careful!)
age -d -i ~/.config/age/keys.txt secrets/env.age
```
## Troubleshooting
### "cannot decrypt: no valid identity"
Your age key isn't configured. Run:
```bash
./setup-dev.sh
```
### "error: flake.nix is not in git"
Commit the flake files:
```bash
git add flake.nix package.nix camera-sdk.nix secrets.nix
git commit -m "Add flake files"
```
### "experimental feature 'flakes' not enabled"
Add to `~/.config/nix/nix.conf`:
```
experimental-features = nix-command flakes
```
## Documentation
- **Full Setup Guide**: [FLAKE_SETUP.md](FLAKE_SETUP.md)
- **Secrets Guide**: [secrets/README.md](secrets/README.md)
- **Migration Summary**: [../NIX_FLAKE_MIGRATION.md](../NIX_FLAKE_MIGRATION.md)
## Questions?
Refer to [FLAKE_SETUP.md](FLAKE_SETUP.md) for detailed documentation, or run:
```bash
./setup-dev.sh # Interactive setup
```
---
**Migration completed on**: 2026-01-30
**Created by**: GitHub Copilot

44
camera-sdk.nix Normal file
View File

@@ -0,0 +1,44 @@
{ stdenv
, lib
, makeWrapper
, libusb1
}:
stdenv.mkDerivation {
pname = "mindvision-camera-sdk";
version = "2.1.0.49";
# Use the camera_sdk directory as source
src = ./camera-management-api/camera_sdk;
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ libusb1 ];
unpackPhase = ''
cp -r $src/* .
tar xzf "linuxSDK_V2.1.0.49(250108).tar.gz"
cd "linuxSDK_V2.1.0.49(250108)"
'';
installPhase = ''
mkdir -p $out/lib $out/include
# Copy x64 library files (SDK has arch-specific subdirs)
if [ -d lib/x64 ]; then
cp -r lib/x64/* $out/lib/ || true
fi
# Copy header files
if [ -d include ]; then
cp -r include/* $out/include/ || true
fi
# Make libraries executable
chmod +x $out/lib/*.so* 2>/dev/null || true
'';
meta = with lib; {
description = "MindVision Camera SDK";
platforms = platforms.linux;
};
}

239
flake.lock generated Normal file
View File

@@ -0,0 +1,239 @@
{
"nodes": {
"agenix": {
"inputs": {
"darwin": "darwin",
"home-manager": "home-manager",
"nixpkgs": [
"ragenix",
"nixpkgs"
],
"systems": "systems_2"
},
"locked": {
"lastModified": 1761656077,
"narHash": "sha256-lsNWuj4Z+pE7s0bd2OKicOFq9bK86JE0ZGeKJbNqb94=",
"owner": "ryantm",
"repo": "agenix",
"rev": "9ba0d85de3eaa7afeab493fed622008b6e4924f5",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"crane": {
"locked": {
"lastModified": 1760924934,
"narHash": "sha256-tuuqY5aU7cUkR71sO2TraVKK2boYrdW3gCSXUkF4i44=",
"owner": "ipetkov",
"repo": "crane",
"rev": "c6b4d5308293d0d04fcfeee92705017537cad02f",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"ragenix",
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1744478979,
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"ragenix",
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1745494811,
"narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"ragenix": {
"inputs": {
"agenix": "agenix",
"crane": "crane",
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1761832913,
"narHash": "sha256-VCNVjjuRvrKPiYYwqhE3BAKIaReiKXGpxGp27lZ0MFM=",
"owner": "yaxitech",
"repo": "ragenix",
"rev": "83bccfdea758241999f32869fb6b36f7ac72f1ac",
"type": "github"
},
"original": {
"owner": "yaxitech",
"repo": "ragenix",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"ragenix": "ragenix"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"ragenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1761791894,
"narHash": "sha256-myRIDh+PxaREz+z9LzbqBJF+SnTFJwkthKDX9zMyddY=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "59c45eb69d9222a4362673141e00ff77842cd219",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

176
flake.nix Normal file
View File

@@ -0,0 +1,176 @@
{
description = "USDA Vision camera management system";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
# For secrets management
ragenix = {
url = "github:yaxitech/ragenix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, flake-utils, ragenix }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
# Import our package definition
usda-vision-package = pkgs.callPackage ./package.nix { };
camera-sdk = pkgs.callPackage ./camera-sdk.nix { };
in
{
packages = {
default = usda-vision-package;
usda-vision = usda-vision-package;
camera-sdk = camera-sdk;
};
devShells.default = pkgs.mkShell {
name = "usda-vision-dev";
# Input packages for the development shell
buildInputs = with pkgs; [
# Core development tools
git
vim
curl
wget
# Docker for local development
docker
docker-compose
# Supabase CLI
supabase-cli
# Node.js for web app development
nodejs_20
nodePackages.npm
nodePackages.pnpm
# Python for camera API
python311
python311Packages.pip
python311Packages.virtualenv
# Camera SDK
camera-sdk
# Secrets management
ragenix.packages.${system}.default
age
ssh-to-age
# Utilities
jq
yq
rsync
gnused
gawk
];
# Environment variables for development
shellHook = ''
export LD_LIBRARY_PATH="${camera-sdk}/lib:$LD_LIBRARY_PATH"
export CAMERA_SDK_PATH="${camera-sdk}"
# Set up Python virtual environment
if [ ! -d .venv ]; then
echo "Creating Python virtual environment..."
python -m venv .venv
fi
echo "USDA Vision Development Environment"
echo "===================================="
echo "Camera SDK: ${camera-sdk}"
echo ""
echo "Available commands:"
echo " - docker-compose: Manage containers"
echo " - supabase: Supabase CLI"
echo " - ragenix: Manage encrypted secrets"
echo " - age: Encrypt/decrypt files"
echo ""
echo "To activate Python venv: source .venv/bin/activate"
echo "To edit secrets: ragenix -e secrets/env.age"
echo ""
'';
# Additional environment configuration
DOCKER_BUILDKIT = "1";
COMPOSE_DOCKER_CLI_BUILD = "1";
};
# NixOS module for easy integration
nixosModules.default = { config, lib, ... }: {
options.services.usda-vision = {
enable = lib.mkEnableOption "USDA Vision camera management system";
secretsFile = lib.mkOption {
type = lib.types.path;
description = "Path to the ragenix-managed secrets file";
};
dataDir = lib.mkOption {
type = lib.types.str;
default = "/var/lib/usda-vision";
description = "Directory for USDA Vision application data";
};
};
config = lib.mkIf config.services.usda-vision.enable {
environment.systemPackages = [
usda-vision-package
camera-sdk
pkgs.docker-compose
];
environment.variables.LD_LIBRARY_PATH = "${camera-sdk}/lib";
virtualisation.docker = {
enable = true;
autoPrune.enable = true;
};
systemd.services.usda-vision = {
description = "USDA Vision Docker Compose Stack";
after = [ "docker.service" "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
# Sync application code
${pkgs.rsync}/bin/rsync -av --delete \
--checksum \
--exclude='node_modules' \
--exclude='.env' \
--exclude='__pycache__' \
--exclude='.venv' \
${usda-vision-package}/opt/usda-vision/ ${config.services.usda-vision.dataDir}/
# Copy secrets if managed by ragenix
if [ -f "${config.services.usda-vision.secretsFile}" ]; then
cp "${config.services.usda-vision.secretsFile}" ${config.services.usda-vision.dataDir}/.env
fi
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
WorkingDirectory = config.services.usda-vision.dataDir;
ExecStart = "${pkgs.docker-compose}/bin/docker-compose up -d --build";
ExecStop = "${pkgs.docker-compose}/bin/docker-compose down";
TimeoutStartSec = 300;
};
};
};
};
}
);
}

131
package.nix Normal file
View File

@@ -0,0 +1,131 @@
{ lib
, stdenv
, makeWrapper
, rsync
, gnused
, gawk
, docker-compose
}:
stdenv.mkDerivation {
pname = "usda-vision";
version = "1.0.0";
# Use the directory from this repository with explicit source filtering
src = lib.cleanSourceWith {
src = ./.;
filter = path: type:
let
baseName = baseNameOf path;
in
# Exclude git, but include everything else
baseName != ".git" &&
baseName != ".cursor" &&
baseName != "__pycache__" &&
baseName != "node_modules" &&
baseName != ".venv" &&
baseName != ".age" &&
baseName != "flake.nix" &&
baseName != "flake.lock" &&
baseName != "package.nix" &&
baseName != "camera-sdk.nix";
};
nativeBuildInputs = [ makeWrapper rsync ];
# Don't run these phases, we'll do everything in installPhase
dontBuild = true;
dontConfigure = true;
installPhase = ''
mkdir -p $out/opt/usda-vision
# Debug: show what's in source
echo "Source directory contents:"
ls -la $src/ || true
# Process docker-compose.yml - replace paths and configure SDK from Nix
if [ -f $src/docker-compose.yml ]; then
# Basic path replacements with sed
${gnused}/bin/sed \
-e 's|env_file:.*management-dashboard-web-app/\.env|env_file: /var/lib/usda-vision/.env|g' \
-e 's|\./management-dashboard-web-app/\.env|/var/lib/usda-vision/.env|g' \
-e 's|\./management-dashboard-web-app|/var/lib/usda-vision/management-dashboard-web-app|g' \
-e 's|\./media-api|/var/lib/usda-vision/media-api|g' \
-e 's|\./video-remote|/var/lib/usda-vision/video-remote|g' \
-e 's|\./scheduling-remote|/var/lib/usda-vision/scheduling-remote|g' \
-e 's|\./vision-system-remote|/var/lib/usda-vision/vision-system-remote|g' \
-e 's|\./camera-management-api|/var/lib/usda-vision/camera-management-api|g' \
$src/docker-compose.yml > $TMPDIR/docker-compose-step1.yml
# Remove SDK installation blocks using awk for better multi-line handling
${gawk}/bin/awk '
/# Only install system packages if not already installed/ { skip=1 }
skip && /^ fi$/ { skip=0; next }
/# Install camera SDK if not already installed/ { skip_sdk=1 }
skip_sdk && /^ fi;$/ { skip_sdk=0; next }
!skip && !skip_sdk { print }
' $TMPDIR/docker-compose-step1.yml > $TMPDIR/docker-compose.yml
rm -f $TMPDIR/docker-compose-step1.yml
fi
# Copy all application files using rsync with chmod
${rsync}/bin/rsync -av \
--chmod=Du+w \
--exclude='.git' \
--exclude='docker-compose.yml' \
--exclude='.env' \
--exclude='*.age' \
--exclude='flake.nix' \
--exclude='flake.lock' \
--exclude='package.nix' \
--exclude='camera-sdk.nix' \
--exclude='management-dashboard-web-app/.env' \
$src/ $out/opt/usda-vision/
# Copy the processed docker-compose.yml
if [ -f $TMPDIR/docker-compose.yml ]; then
cp $TMPDIR/docker-compose.yml $out/opt/usda-vision/docker-compose.yml
fi
# Verify files were copied
echo "Destination directory contents:"
ls -la $out/opt/usda-vision/ || true
# Create convenience scripts
mkdir -p $out/bin
cat > $out/bin/usda-vision-start <<'EOF'
#!/usr/bin/env bash
cd $out/opt/usda-vision
${docker-compose}/bin/docker-compose up -d --build
EOF
cat > $out/bin/usda-vision-stop <<'EOF'
#!/usr/bin/env bash
cd $out/opt/usda-vision
${docker-compose}/bin/docker-compose down
EOF
cat > $out/bin/usda-vision-logs <<'EOF'
#!/usr/bin/env bash
cd $out/opt/usda-vision
${docker-compose}/bin/docker-compose logs -f "$@"
EOF
cat > $out/bin/usda-vision-restart <<'EOF'
#!/usr/bin/env bash
cd $out/opt/usda-vision
${docker-compose}/bin/docker-compose restart "$@"
EOF
chmod +x $out/bin/usda-vision-*
'';
meta = with lib; {
description = "USDA Vision camera management system";
maintainers = [ "UGA Innovation Factory" ];
platforms = platforms.linux;
};
}

14
secrets.nix Normal file
View File

@@ -0,0 +1,14 @@
# Ragenix Configuration
# This file defines which secrets to manage and their permissions
let
# Import public keys from secrets.nix
keys = import ./secrets.nix;
in
{
# Main environment file
"env.age".publicKeys = keys.publicKeys;
# Azure OAuth configuration
"env.azure.age".publicKeys = keys.publicKeys;
}

11
secrets/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
# Ignore unencrypted secrets
*.env
!*.env.example
.env.*
!.env.*.example
# Ignore age private keys (if accidentally placed here)
*.txt
# Keep encrypted files
!*.age

75
secrets/README.md Normal file
View File

@@ -0,0 +1,75 @@
# USDA Vision Secrets Management
This directory contains encrypted secrets managed by [ragenix](https://github.com/yaxitech/ragenix).
## Setup
1. **Generate an age key** (if you don't have one):
```bash
# Generate a new age key
age-keygen -o ~/.config/age/keys.txt
# Or convert your SSH key
ssh-to-age < ~/.ssh/id_ed25519.pub
```
2. **Add your public key to `secrets.nix`**:
```nix
{
publicKeys = [
"age1..." # Your age public key
"ssh-ed25519 ..." # Or your SSH public key
];
}
```
3. **Create and encrypt environment files**:
```bash
# Create the encrypted .env file
ragenix -e secrets/env.age
# Create the encrypted .env.azure file
ragenix -e secrets/env.azure.age
```
## Usage in Development
In the development shell:
```bash
# Edit encrypted secrets
ragenix -e secrets/env.age
# Re-key secrets after adding a new public key
ragenix -r
```
## Usage in NixOS
The flake's NixOS module automatically handles decryption:
```nix
{
services.usda-vision = {
enable = true;
secretsFile = config.age.secrets.usda-vision-env.path;
};
age.secrets.usda-vision-env = {
file = ./usda-vision/secrets/env.age;
mode = "0644";
};
}
```
## Files
- `secrets.nix` - Public keys configuration
- `env.age` - Encrypted main .env file
- `env.azure.age` - Encrypted Azure OAuth configuration
- `README.md` - This file
## Security Notes
- Never commit unencrypted `.env` files
- Keep your age private key secure (`~/.config/age/keys.txt`)
- The `.age` encrypted files are safe to commit to git

14
secrets/secrets.nix Normal file
View File

@@ -0,0 +1,14 @@
# Public keys for secret encryption
# Add your age or SSH public keys here
{
publicKeys = [
# Example age public key:
# "age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3ekg8p"
# Example SSH public key (ed25519):
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... user@host"
# Add your keys below:
# TODO: Add your age or SSH public keys
];
}

77
setup-dev.sh Executable file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# Quick setup script for USDA Vision development
set -e
echo "======================================"
echo "USDA Vision - Quick Setup"
echo "======================================"
echo ""
# Check if we're in the right directory
if [ ! -f "flake.nix" ]; then
echo "❌ Error: Must run from usda-vision directory"
echo " cd to the directory containing flake.nix"
exit 1
fi
# Check for age key
if [ ! -f "$HOME/.config/age/keys.txt" ]; then
echo "📝 No age key found at ~/.config/age/keys.txt"
echo ""
read -p "Would you like to generate one? (y/n) " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
mkdir -p "$HOME/.config/age"
age-keygen -o "$HOME/.config/age/keys.txt"
echo "✅ Age key generated!"
echo ""
else
echo "❌ Cannot proceed without an age key"
exit 1
fi
fi
# Get public key
AGE_PUBLIC_KEY=$(grep "public key:" "$HOME/.config/age/keys.txt" | cut -d: -f2 | xargs)
echo "Your age public key is:"
echo " $AGE_PUBLIC_KEY"
echo ""
# Check if key is already in secrets.nix
if grep -q "$AGE_PUBLIC_KEY" secrets/secrets.nix 2>/dev/null; then
echo "✅ Your key is already in secrets/secrets.nix"
else
echo "⚠️ Your key is NOT in secrets/secrets.nix"
echo ""
read -p "Would you like to add it now? (y/n) " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
# Backup original
cp secrets/secrets.nix secrets/secrets.nix.backup
# Add the key
sed -i "/publicKeys = \[/a\ \"$AGE_PUBLIC_KEY\"" secrets/secrets.nix
echo "✅ Key added to secrets/secrets.nix"
echo ""
fi
fi
echo "======================================"
echo "Setup complete! Next steps:"
echo "======================================"
echo ""
echo "1. Enter development environment:"
echo " $ nix develop"
echo ""
echo "2. Create/edit encrypted secrets:"
echo " $ ragenix -e secrets/env.age"
echo " $ ragenix -e secrets/env.azure.age"
echo ""
echo "3. Start development:"
echo " $ docker-compose up -d"
echo ""
echo "For more information, see FLAKE_SETUP.md"
echo ""