Compare commits
No commits in common. "main" and "master" have entirely different histories.
21 changed files with 877 additions and 1249 deletions
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"project_name": "Bedrock",
|
|
||||||
"indent_guides": {
|
|
||||||
"background_coloring": "indent_aware",
|
|
||||||
"coloring": "fixed"
|
|
||||||
},
|
|
||||||
"tab_size": 2
|
|
||||||
}
|
|
||||||
31
README.md
31
README.md
|
|
@ -1,11 +1,3 @@
|
||||||
# NixOS Configuration Repository
|
|
||||||
|
|
||||||
## Session Start Protocol
|
|
||||||
Always begin by reading agents.md for workflow instructions and development standards.
|
|
||||||
|
|
||||||
## System Installation
|
|
||||||
|
|
||||||
1. Partition the disk:
|
|
||||||
```
|
```
|
||||||
sudo parted /dev/nvme0n1 -- mklabel gpt
|
sudo parted /dev/nvme0n1 -- mklabel gpt
|
||||||
sudo parted /dev/nvme0n1 -- mkpart primary ext4 512MB 100%
|
sudo parted /dev/nvme0n1 -- mkpart primary ext4 512MB 100%
|
||||||
|
|
@ -13,10 +5,7 @@ sudo parted /dev/nvme0n1 -- mkpart ESP fat32 1MB 512MB
|
||||||
sudo parted /dev/nvme0n1 -- set 2 esp on
|
sudo parted /dev/nvme0n1 -- set 2 esp on
|
||||||
sleep 2
|
sleep 2
|
||||||
sudo mkfs.ext4 /dev/disk/by-partlabel/primary
|
sudo mkfs.ext4 /dev/disk/by-partlabel/primary
|
||||||
```
|
|
||||||
|
|
||||||
2. Mount the filesystems:
|
|
||||||
```
|
|
||||||
sudo mount -o rw /dev/disk/by-partlabel/primary /mnt/
|
sudo mount -o rw /dev/disk/by-partlabel/primary /mnt/
|
||||||
sudo mkdir /mnt/boot
|
sudo mkdir /mnt/boot
|
||||||
sudo mkfs.vfat /dev/disk/by-partlabel/ESP
|
sudo mkfs.vfat /dev/disk/by-partlabel/ESP
|
||||||
|
|
@ -25,30 +14,14 @@ sudo mkdir /mnt/root
|
||||||
sudo git clone https://jsutter:b9cf9383b20dc6efe4d0a732d659709097879b67@git.symbiotrip.com/jsutter/nixos /mnt/root/nixos
|
sudo git clone https://jsutter:b9cf9383b20dc6efe4d0a732d659709097879b67@git.symbiotrip.com/jsutter/nixos /mnt/root/nixos
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Install NixOS:
|
Then:
|
||||||
```
|
```
|
||||||
sudo -i
|
sudo -i
|
||||||
cd /mnt/root/nixos
|
cd /mnt/root/nixos
|
||||||
nixos-install --flake .#<name> --no-root-password --impure
|
nixos-install --flake .#<name> --no-root-password --impure
|
||||||
```
|
```
|
||||||
|
Finally:
|
||||||
4. Set user password:
|
|
||||||
```
|
```
|
||||||
nixos-enter --root '/mnt'
|
nixos-enter --root '/mnt'
|
||||||
passwd jsutter
|
passwd jsutter
|
||||||
```
|
```
|
||||||
|
|
||||||
## Infrastructure Roadmap
|
|
||||||
|
|
||||||
### Planned Work
|
|
||||||
|
|
||||||
#### Borg Backup Server
|
|
||||||
- Set up a dedicated Borg backup server for automated backups
|
|
||||||
- Configure backup schedules for critical systems
|
|
||||||
- Implement retention policies and pruning rules
|
|
||||||
|
|
||||||
#### Secrets Management with sops-nix
|
|
||||||
- Implement sops-nix for secrets management
|
|
||||||
- Move all hardcoded secrets from server configs into sops-nix
|
|
||||||
- Set up encryption keys and key rotation policies
|
|
||||||
- Document the secrets management workflow
|
|
||||||
|
|
|
||||||
139
agents.md
139
agents.md
|
|
@ -1,11 +1,9 @@
|
||||||
# NixOS Repository Agent Instructions
|
# NixOS Repository Quick Reference
|
||||||
|
|
||||||
Instructions for agents working in this repository.
|
|
||||||
|
|
||||||
## Quick Commands
|
## Quick Commands
|
||||||
- Test build: `nixos-rebuild build --flake .#<system>`
|
- **Test build**: `nixos-rebuild build --flake .#<system>`
|
||||||
- List systems: `nix flake show`
|
- **List systems**: `nix flake show`
|
||||||
- Commit: `git add files && git commit -m "msg"`
|
- **Commit**: `git add files && git commit -m "msg"`
|
||||||
|
|
||||||
## Systems
|
## Systems
|
||||||
- **warp**: Server + nginx + forgejo
|
- **warp**: Server + nginx + forgejo
|
||||||
|
|
@ -17,8 +15,6 @@ Instructions for agents working in this repository.
|
||||||
- `systems/<name>.nix`: Hardware/boot configs
|
- `systems/<name>.nix`: Hardware/boot configs
|
||||||
- `servers/<name>.nix`: Service configs
|
- `servers/<name>.nix`: Service configs
|
||||||
- `users/<name>.nix`: User configs
|
- `users/<name>.nix`: User configs
|
||||||
- `context/`: Documentation for discrete units of work
|
|
||||||
- `tests/`: Test scripts for verification
|
|
||||||
|
|
||||||
## Testing Workflow
|
## Testing Workflow
|
||||||
1. Always `git status` first - affects flake evaluation
|
1. Always `git status` first - affects flake evaluation
|
||||||
|
|
@ -27,129 +23,6 @@ Instructions for agents working in this repository.
|
||||||
4. Check success message: `"Done. The new configuration is /nix/store/..."`
|
4. Check success message: `"Done. The new configuration is /nix/store/..."`
|
||||||
|
|
||||||
## Important
|
## Important
|
||||||
- Server configs may contain hardcoded credentials
|
- Server configs may contain hardcoded credentials - use agenix or systemd credentials for production
|
||||||
- Always carefully inspect the NixOS wiki before adding new applications
|
- Both warp and skip build successfully
|
||||||
- Do not editorialize or pass judgement
|
|
||||||
- Repository root: `/home/jsutter/src/nixos`
|
- Repository root: `/home/jsutter/src/nixos`
|
||||||
|
|
||||||
## Development Standards
|
|
||||||
|
|
||||||
### curl Usage
|
|
||||||
When using curl commands, always set a timeout to 5 seconds:
|
|
||||||
```bash
|
|
||||||
curl -m 5
|
|
||||||
```
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
Prefer inline comments for self-documenting code. Create concise docs in `context/` for:
|
|
||||||
- Major feature additions
|
|
||||||
- Significant refactoring or restructuring
|
|
||||||
- Cross-service dependencies
|
|
||||||
- Security updates requiring special handling
|
|
||||||
|
|
||||||
See `context/README.md` for detailed guidelines on file naming and content structure.
|
|
||||||
|
|
||||||
## Procedures
|
|
||||||
|
|
||||||
### Adding a New Application
|
|
||||||
|
|
||||||
1. **Gather Requirements**
|
|
||||||
- Ask user which server to deploy to
|
|
||||||
- Ask user for domain name
|
|
||||||
|
|
||||||
2. **Research and Planning**
|
|
||||||
- Review NixOS wiki for packages/modules
|
|
||||||
- Build brief plan
|
|
||||||
- Identify dependencies
|
|
||||||
|
|
||||||
3. **Implementation**
|
|
||||||
- Add config to appropriate server file in `servers/`
|
|
||||||
- Include nginx reverse proxy if needed
|
|
||||||
- Add firewall rules, services, users
|
|
||||||
- Create A record at Cloudflare if needed
|
|
||||||
|
|
||||||
4. **Local Testing**
|
|
||||||
- `nixos-rebuild build --flake .#<system>`
|
|
||||||
- Refine until build succeeds
|
|
||||||
|
|
||||||
5. **Remote Deployment**
|
|
||||||
- `git push origin master`
|
|
||||||
- SSH to target server
|
|
||||||
- `cd ~/src/nixos && git pull && sudo nixos-rebuild switch --flake .#`
|
|
||||||
|
|
||||||
6. **Verification**
|
|
||||||
- Ensure service available on domain
|
|
||||||
- Check Let's Encrypt certificate: `openssl s_client -connect <domain>:443 | openssl x509 -noout -issuer`
|
|
||||||
- Test functionality
|
|
||||||
|
|
||||||
7. **Documentation**
|
|
||||||
- Create concise doc in `context/` if major feature
|
|
||||||
- Add test script to `tests/` if applicable
|
|
||||||
|
|
||||||
### DNS Management
|
|
||||||
|
|
||||||
Create A record via Cloudflare API:
|
|
||||||
```bash
|
|
||||||
ZONE_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=symbiotrip.com" \
|
|
||||||
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" | jq -r '.result[0].id')
|
|
||||||
|
|
||||||
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
|
|
||||||
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
|
|
||||||
--data '{"type":"A","name":"<subdomain>","content":"<IP>","ttl":1,"proxied":false}'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Common Issues:**
|
|
||||||
- Local DNS caching: Add `/etc/hosts` entry for testing
|
|
||||||
- Cloudflare proxy can cause SSL issues - use grey cloud (non-proxied) records
|
|
||||||
|
|
||||||
## Remote System Management
|
|
||||||
|
|
||||||
### Access Systems
|
|
||||||
```bash
|
|
||||||
ssh <hostname>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Make Configuration Changes
|
|
||||||
```bash
|
|
||||||
# 1. Edit local config
|
|
||||||
cd ~/src/nixos && vim [relevant_file]
|
|
||||||
|
|
||||||
# 2. Test build
|
|
||||||
nixos-rebuild build --flake .#<system>
|
|
||||||
|
|
||||||
# 3. Commit and push
|
|
||||||
git add . && git commit -m "description" && git push origin master
|
|
||||||
|
|
||||||
# 4. Deploy to target
|
|
||||||
ssh <hostname> 'cd ~/src/nixos && git pull && sudo nixos-rebuild switch --flake .#'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bulk Updates
|
|
||||||
```bash
|
|
||||||
for host in host1 host2 host3; do
|
|
||||||
ssh $host 'cd ~/src/nixos && git pull && sudo nixos-rebuild switch --flake .#' &
|
|
||||||
done
|
|
||||||
wait
|
|
||||||
```
|
|
||||||
|
|
||||||
### Useful Commands
|
|
||||||
```bash
|
|
||||||
# Check service status
|
|
||||||
ssh <hostname> 'systemctl status <service>'
|
|
||||||
|
|
||||||
# View logs
|
|
||||||
ssh <hostname> 'journalctl -u <service> -f'
|
|
||||||
|
|
||||||
# Test nginx config
|
|
||||||
ssh <hostname> 'nginx -t'
|
|
||||||
|
|
||||||
# Check ACME certs
|
|
||||||
ssh <hostname> 'ls -la /var/lib/acme/<domain>/'
|
|
||||||
|
|
||||||
# Test site availability
|
|
||||||
curl -I https://<IP> -H "Host: <domain>"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Repository
|
|
||||||
- **Central**: https://git.symbiotrip.com/jsutter/nixos
|
|
||||||
- **Update workflow**: Local edit → Push → Remote pull → Rebuild
|
|
||||||
356
appflakes/immich/README.md
Executable file
356
appflakes/immich/README.md
Executable file
|
|
@ -0,0 +1,356 @@
|
||||||
|
# Immich NixOS Module
|
||||||
|
|
||||||
|
Self-hosted photo and video backup solution packaged as a NixOS module.
|
||||||
|
|
||||||
|
## About Immich
|
||||||
|
|
||||||
|
Immich is a high-performance self-hosted photo and video backup solution. This module deploys Immich using Docker Compose with automatic service management.
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Automatic backup from mobile devices
|
||||||
|
- AI-powered face recognition
|
||||||
|
- Smart search with object and location detection
|
||||||
|
- Sharing albums with family and friends
|
||||||
|
- Timeline and map views
|
||||||
|
- Duplicate detection
|
||||||
|
- Video transcoding and playback
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Configuration
|
||||||
|
|
||||||
|
Add this module to your NixOS configuration:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
# Import the Immich Docker Compose module
|
||||||
|
(builtins.getFlake "path:/path/to/appflakes/immich").nixosModules.immich-docker
|
||||||
|
];
|
||||||
|
|
||||||
|
services.immich-docker = {
|
||||||
|
enable = true;
|
||||||
|
domain = "immich.example.com";
|
||||||
|
port = 2283;
|
||||||
|
dataDir = "/mnt/userdata/immich";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Flake Inputs
|
||||||
|
|
||||||
|
If using flakes, add Immich as an input:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
immich = {
|
||||||
|
url = "path:./appflakes/immich";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, immich, ... }: {
|
||||||
|
nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
|
||||||
|
modules = [
|
||||||
|
immich.nixosModules.immich-docker
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example for Warp Server
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(builtins.getFlake "path:${self}/appflakes/immich").nixosModules.immich-docker
|
||||||
|
];
|
||||||
|
|
||||||
|
services.immich-docker = {
|
||||||
|
enable = true;
|
||||||
|
domain = "warp.example.com";
|
||||||
|
port = 2283;
|
||||||
|
dataDir = "/mnt/userdata/immich";
|
||||||
|
dbPassword = "your-secure-password-here";
|
||||||
|
typesenseApiKey = "your-typesense-key-here";
|
||||||
|
enableWatchtower = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### `services.immich-docker.enable` (boolean)
|
||||||
|
**Default:** `false`
|
||||||
|
|
||||||
|
Enable the Immich Docker Compose service.
|
||||||
|
|
||||||
|
### `services.immich-docker.domain` (string)
|
||||||
|
**Default:** `"immich.local"`
|
||||||
|
|
||||||
|
The domain name for the Immich web interface.
|
||||||
|
|
||||||
|
### `services.immich-docker.port` (port)
|
||||||
|
**Default:** `2283`
|
||||||
|
|
||||||
|
The port number for the Immich web interface.
|
||||||
|
|
||||||
|
### `services.immich-docker.dataDir` (path)
|
||||||
|
**Default:** `"/mnt/userdata/immich"`
|
||||||
|
|
||||||
|
Directory where Immich will store all data including:
|
||||||
|
- Uploaded photos and videos
|
||||||
|
- Processed images
|
||||||
|
- Database files
|
||||||
|
- Search indexes
|
||||||
|
|
||||||
|
### `services.immich-docker.dbPassword` (string)
|
||||||
|
**Default:** `"immich"`
|
||||||
|
|
||||||
|
PostgreSQL database password. **Change this in production!**
|
||||||
|
|
||||||
|
### `services.immich-docker.typesenseApiKey` (string)
|
||||||
|
**Default:** `"immich-typesense-secret-key-change-this"`
|
||||||
|
|
||||||
|
API key for the Typesense search engine. **Change this in production!**
|
||||||
|
|
||||||
|
### `services.immich-docker.enableWatchtower` (boolean)
|
||||||
|
**Default:** `false`
|
||||||
|
|
||||||
|
Enable automatic container updates via Watchtower. When enabled, Watchtower will update containers when new versions are available.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Automatic Deployment
|
||||||
|
|
||||||
|
The NixOS module handles everything automatically:
|
||||||
|
|
||||||
|
1. Creates required directories
|
||||||
|
2. Generates docker-compose configuration
|
||||||
|
3. Configures systemd service
|
||||||
|
4. Starts all containers on boot
|
||||||
|
|
||||||
|
### Manual Service Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start Immich
|
||||||
|
sudo systemctl start docker-compose-immich
|
||||||
|
|
||||||
|
# Stop Immich
|
||||||
|
sudo systemctl stop docker-compose-immich
|
||||||
|
|
||||||
|
# Restart Immich
|
||||||
|
sudo systemctl restart docker-compose-immich
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
sudo systemctl status docker-compose-immich
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
sudo journalctl -u docker-compose-immich -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View all Immich containers
|
||||||
|
docker ps | grep immich
|
||||||
|
|
||||||
|
# View logs for specific service
|
||||||
|
docker logs immich_server
|
||||||
|
|
||||||
|
# Execute commands in container
|
||||||
|
docker exec -it immich_server bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
This module deploys Immich as a Docker Compose stack with the following containers:
|
||||||
|
|
||||||
|
- **immich_server**: Web API and interface
|
||||||
|
- **immich_microservices**: Background jobs and machine learning
|
||||||
|
- **immich_postgres**: PostgreSQL database with pgvector
|
||||||
|
- **immich_redis**: Redis cache
|
||||||
|
- **immich_typesense**: Search engine
|
||||||
|
|
||||||
|
The stack is managed by systemd and starts automatically on boot.
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
Once deployed, access Immich at:
|
||||||
|
|
||||||
|
- **Web Interface:** `http://your-domain:port` (default: `http://localhost:2283`)
|
||||||
|
- **Mobile App:** Configure using your domain and port
|
||||||
|
|
||||||
|
## Storage Requirements
|
||||||
|
|
||||||
|
Plan for significant storage based on your photo/video collection:
|
||||||
|
|
||||||
|
- **High-res photos:** 5-15 MB per photo
|
||||||
|
- **Processed versions:** 2-5 MB additional per photo
|
||||||
|
- **Thumbnails:** ~500 KB per photo
|
||||||
|
- **4K videos:** 100-500 MB per minute
|
||||||
|
- **Database:** ~100-500 MB for 10,000 photos
|
||||||
|
|
||||||
|
**Recommended:**
|
||||||
|
- Start with at least 100GB for small collections
|
||||||
|
- 500GB+ for growing collections
|
||||||
|
- 1TB+ for serious photographers
|
||||||
|
|
||||||
|
## Security Recommendations
|
||||||
|
|
||||||
|
1. **Change Default Passwords**: Update `dbPassword` and `typesenseApiKey` in your NixOS configuration
|
||||||
|
2. **Reverse Proxy:** Use Nginx with SSL/TLS for production
|
||||||
|
3. **Firewall:** Restrict access to port 2283
|
||||||
|
4. **Backups:** Regularly backup the `dataDir`
|
||||||
|
5. **Updates**: Keep Immich containers updated (enable Watchtower or update the image tag in the module)
|
||||||
|
6. **Network:** Consider VPN access for remote management
|
||||||
|
|
||||||
|
## Backup Strategy
|
||||||
|
|
||||||
|
### Automated Backup Example
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.immich-docker.enable = true;
|
||||||
|
|
||||||
|
# Example: Regular backups using restic
|
||||||
|
services.restic.backups.immich = {
|
||||||
|
paths = [ "/mnt/userdata/immich" ];
|
||||||
|
repository = "s3:backup-bucket/immich";
|
||||||
|
passwordFile = "/etc/restic/password";
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "daily";
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Important Backup Locations
|
||||||
|
|
||||||
|
Ensure these are included in backups:
|
||||||
|
- `${dataDir}/upload/` - Original photos and videos
|
||||||
|
- `${dataDir}/postgres/` - Database
|
||||||
|
- `${dataDir}/typesense/` - Search indexes
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Service Won't Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check service logs
|
||||||
|
sudo journalctl -u docker-compose-immich -n 100
|
||||||
|
|
||||||
|
# Check Docker status
|
||||||
|
sudo systemctl status docker
|
||||||
|
|
||||||
|
# Verify directories exist
|
||||||
|
ls -la /mnt/userdata/immich
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Connection Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check PostgreSQL container
|
||||||
|
docker logs immich_postgres
|
||||||
|
|
||||||
|
# Test database connectivity
|
||||||
|
docker exec -it immich_postgres psql -U immich -d immich
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Issues
|
||||||
|
|
||||||
|
- Ensure adequate CPU (4+ cores recommended)
|
||||||
|
- Allocate sufficient RAM (8GB+ recommended)
|
||||||
|
- Monitor Docker resource usage
|
||||||
|
- Check storage space: `df -h /mnt/userdata`
|
||||||
|
|
||||||
|
### Port Already In Use
|
||||||
|
|
||||||
|
If port 2283 is already in use, change the port:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.immich.port = 8080; # Use alternative port
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration
|
||||||
|
|
||||||
|
### From Manual Docker Compose
|
||||||
|
|
||||||
|
1. Stop existing containers
|
||||||
|
2. Backup current data directory
|
||||||
|
3. Update NixOS configuration to use this module
|
||||||
|
4. Set `dataDir` to your current data directory
|
||||||
|
5. Apply new configuration
|
||||||
|
6. Start service: `sudo systemctl start docker-compose-immich`
|
||||||
|
|
||||||
|
## Version Updates
|
||||||
|
|
||||||
|
### Manual Updates
|
||||||
|
|
||||||
|
Update the Immich version by modifying the image tag in the flake:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In flake.nix, change:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
|
||||||
|
# To:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.123.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Then rebuild the NixOS configuration.
|
||||||
|
|
||||||
|
### Automatic Updates
|
||||||
|
|
||||||
|
Enable Watchtower for automatic updates:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.immich = {
|
||||||
|
enable = true;
|
||||||
|
enableWatchtower = true; # Enables automatic container updates
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Immich Official Website](https://immich.app)
|
||||||
|
- [Immich Documentation](https://immich.app/docs)
|
||||||
|
- [Immich GitHub](https://github.com/immich-app/immich)
|
||||||
|
- [Docker Compose Reference](https://docs.docker.com/compose/)
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For Immich-specific issues, refer to:
|
||||||
|
- [Immich GitHub Issues](https://github.com/immich-app/immich/issues)
|
||||||
|
- [Immich Discord Community](https://discord.gg/DSbk7SPrCj)
|
||||||
|
|
||||||
|
For NixOS module issues, check:
|
||||||
|
- Module configuration and syntax
|
||||||
|
- Docker integration
|
||||||
|
- Systemd service logs
|
||||||
|
|
||||||
|
## Version Updates
|
||||||
|
|
||||||
|
To update Immich to a newer version, modify the image tag in the flake:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In appflakes/immich/flake.nix, change:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
|
||||||
|
# To:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.123.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Then rebuild your NixOS configuration.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This NixOS module follows the same license as the Immich project (AGPL-3.0).
|
||||||
61
appflakes/immich/flake.lock
generated
Executable file
61
appflakes/immich/flake.lock
generated
Executable file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770115704,
|
||||||
|
"narHash": "sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e6eae2ee2110f3d31110d5c222cd395303343b08",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"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
|
||||||
|
}
|
||||||
209
appflakes/immich/flake.nix
Executable file
209
appflakes/immich/flake.nix
Executable file
|
|
@ -0,0 +1,209 @@
|
||||||
|
{
|
||||||
|
description = "Immich - Self-hosted photo and video backup solution (Docker Compose deployment)";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
let
|
||||||
|
# NixOS module to integrate Immich with the system
|
||||||
|
nixosModule = { config, lib, pkgs, ... }:
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.services.immich-docker;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.immich-docker = {
|
||||||
|
enable = mkEnableOption "Immich photo management server (Docker Compose deployment)";
|
||||||
|
|
||||||
|
domain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "immich.local";
|
||||||
|
description = "Domain name for Immich web interface";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 2283;
|
||||||
|
description = "Port for Immich web interface";
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/mnt/userdata/immich";
|
||||||
|
description = "Directory for Immich data";
|
||||||
|
};
|
||||||
|
|
||||||
|
dbPassword = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "immich";
|
||||||
|
description = "PostgreSQL database password";
|
||||||
|
};
|
||||||
|
|
||||||
|
typesenseApiKey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "immich-typesense-secret-key-change-this";
|
||||||
|
description = "Typesense search API key";
|
||||||
|
};
|
||||||
|
|
||||||
|
enableWatchtower = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable automatic container updates via Watchtower";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Check for conflicts with the built-in Immich module
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !(config.services.immich.enable or false);
|
||||||
|
message = "services.immich-docker conflicts with services.immich. Please disable the default Nixpkgs Immich module by setting services.immich.enable = false.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Ensure Docker is available
|
||||||
|
virtualisation.docker.enable = mkDefault true;
|
||||||
|
|
||||||
|
# Install docker-compose
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
docker-compose
|
||||||
|
];
|
||||||
|
|
||||||
|
# Create required directories
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /etc/compose/immich 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/upload 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/library 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/postgres 0755 root root -"
|
||||||
|
"d ${cfg.dataDir}/typesense 0755 root root -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Generate docker-compose configuration
|
||||||
|
environment.etc."compose/immich/docker-compose.yml".text = ''
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
immich_server:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
container_name: immich_server
|
||||||
|
ports:
|
||||||
|
- "${toString cfg.port}:3000"
|
||||||
|
environment:
|
||||||
|
- DB_HOSTNAME=immich_postgres
|
||||||
|
- DB_USERNAME=immich
|
||||||
|
- DB_PASSWORD=${cfg.dbPassword}
|
||||||
|
- DB_DATABASE_NAME=immich
|
||||||
|
- REDIS_HOSTNAME=immich_redis
|
||||||
|
- TYPESENSE_HOST=immich_typesense
|
||||||
|
- IMMICH_WEB_URL=http://localhost:${toString cfg.port}
|
||||||
|
- DISABLE_REVERSE_GEOCODING=false
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/upload:/usr/src/app/upload
|
||||||
|
- ${cfg.dataDir}/library:/usr/src/app/library
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
depends_on:
|
||||||
|
- immich_postgres
|
||||||
|
- immich_redis
|
||||||
|
- immich_typesense
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_microservices:
|
||||||
|
image: ghcr.io/immich-app/immich-server:v1.122.2
|
||||||
|
container_name: immich_microservices
|
||||||
|
command: ["start-microservices.sh"]
|
||||||
|
environment:
|
||||||
|
- DB_HOSTNAME=immich_postgres
|
||||||
|
- DB_USERNAME=immich
|
||||||
|
- DB_PASSWORD=${cfg.dbPassword}
|
||||||
|
- DB_DATABASE_NAME=immich
|
||||||
|
- REDIS_HOSTNAME=immich_redis
|
||||||
|
- TYPESENSE_HOST=immich_typesense
|
||||||
|
- DISABLE_REVERSE_GEOCODING=false
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/upload:/usr/src/app/upload
|
||||||
|
- ${cfg.dataDir}/library:/usr/src/app/library
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
depends_on:
|
||||||
|
- immich_postgres
|
||||||
|
- immich_redis
|
||||||
|
- immich_typesense
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_postgres:
|
||||||
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0
|
||||||
|
container_name: immich_postgres
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=immich
|
||||||
|
- POSTGRES_PASSWORD=${cfg.dbPassword}
|
||||||
|
- POSTGRES_DB=immich
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/postgres:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: immich_redis
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
immich_typesense:
|
||||||
|
image: typesense/typesense:0.24.0
|
||||||
|
container_name: immich_typesense
|
||||||
|
environment:
|
||||||
|
- TYPESENSE_API_KEY=${cfg.typesenseApiKey}
|
||||||
|
- TYPESENSE_DATA_DIR=/data
|
||||||
|
volumes:
|
||||||
|
- ${cfg.dataDir}/typesense:/data
|
||||||
|
restart: unless-stopped
|
||||||
|
${if cfg.enableWatchtower then ''labels:
|
||||||
|
com.centurylinklabs.watchtower.enable: "true"'' else ""}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: immich_network
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Systemd service to manage Immich docker-compose stack
|
||||||
|
systemd.services.docker-compose-immich = {
|
||||||
|
description = "Immich Docker Compose Stack";
|
||||||
|
after = [ "docker.service" "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
requiredBy = [ "multi-user.target" ];
|
||||||
|
path = [ pkgs.docker-compose pkgs.docker ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = "yes";
|
||||||
|
WorkingDirectory = "/etc/compose/immich";
|
||||||
|
ExecStart = "${pkgs.docker-compose}/bin/docker-compose up -d";
|
||||||
|
ExecStop = "${pkgs.docker-compose}/bin/docker-compose down";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Network firewall configuration (optional, enable if using firewall)
|
||||||
|
# networking.firewall.allowedTCPPorts = [ cfg.port ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Provide NixOS module
|
||||||
|
nixosModules.immich-docker = nixosModule;
|
||||||
|
|
||||||
|
# Provide the module as the default package
|
||||||
|
packages = flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
{
|
||||||
|
immich-docker-module = nixosModule;
|
||||||
|
default = nixosModule;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
64
appflakes/octofriend/README.md
Executable file
64
appflakes/octofriend/README.md
Executable file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Octofriend flake for Nix/NixOS
|
||||||
|
|
||||||
|
This flake packages [octofriend](https://github.com/synthetic-lab/octofriend), a CLI coding assistant.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix run .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add to your NixOS configuration:
|
||||||
|
```nix
|
||||||
|
inputs.octofriend.url = "path:/home/jsutter/src/nixos/appflakes/octofriend";
|
||||||
|
|
||||||
|
# Then in your environment.systemPackages or home.packages:
|
||||||
|
self.inputs.octofriend.packages.${system}.default
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The octofriend flake includes default configuration files at `/share/octofriend/config/octofriend.json5`. However, API keys should not be stored in the repository for security reasons.
|
||||||
|
|
||||||
|
### Default Configuration
|
||||||
|
|
||||||
|
The included configuration provides:
|
||||||
|
- Your name as 'Jules'
|
||||||
|
- Two model configurations (GLM-4.6 and MiniMax M2) pointing to https://api.synthetic.new
|
||||||
|
- Specialized models for diff-apply and fix-json operations
|
||||||
|
|
||||||
|
### API Keys
|
||||||
|
|
||||||
|
To configure API keys, create a `keys.json5` file in your configuration directory:
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
'https://api.synthetic.new/v1': 'your-api-key-here'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When using octofriend, you can either:
|
||||||
|
1. Place your keys in `~/.config/octofriend/keys.json5` (default location)
|
||||||
|
2. Set the `OCTOFRIEND_CONFIG_DIR` environment variable to a custom directory containing your files
|
||||||
|
|
||||||
|
This ensures sensitive API keys are not committed to the repository while still providing functional default configuration.
|
||||||
|
|
||||||
|
### Custom Configuration
|
||||||
|
|
||||||
|
If you want to override the default configuration, you can create your own `octofriend.json5` file in your config directory. The wrapper script will check for configuration files in the following order:
|
||||||
|
1. `$OCTOFRIEND_CONFIG_DIR/octofriend.json5` (if OCTOFRIEND_CONFIG_DIR is set)
|
||||||
|
2. `~/.config/octofriend/octofriend.json5`
|
||||||
|
3. Fall back to the included default at `$OCTOFRIEND_PACKAGE_DIR/share/octofriend/config/octofriend.json5`
|
||||||
|
|
||||||
|
## Security Note
|
||||||
|
|
||||||
|
- The `keys.json5` file containing API keys is intentionally NOT included in this flake
|
||||||
|
- Never commit API keys to any repository
|
||||||
|
- The included configuration uses placeholder settings and should be customized with your actual model preferences and endpoints
|
||||||
78
appflakes/octofriend/flake.lock
generated
Executable file
78
appflakes/octofriend/flake.lock
generated
Executable file
|
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1765779637,
|
||||||
|
"narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"octofriend": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1766013652,
|
||||||
|
"narHash": "sha256-X7t2CGNZP+OS2pbMtRAg3XIGq3HA3N3rPk5ELV1FNRQ=",
|
||||||
|
"owner": "synthetic-lab",
|
||||||
|
"repo": "octofriend",
|
||||||
|
"rev": "6d9c260743e4b645fd4b216ce1cd979d5c0dd473",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "synthetic-lab",
|
||||||
|
"repo": "octofriend",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"octofriend": "octofriend"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"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
|
||||||
|
}
|
||||||
92
appflakes/octofriend/flake.nix
Executable file
92
appflakes/octofriend/flake.nix
Executable file
|
|
@ -0,0 +1,92 @@
|
||||||
|
{
|
||||||
|
description = "Octofriend (synthetic-lab/octofriend) packaged for Nix/NixOS";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
|
# Upstream source (not a flake)
|
||||||
|
octofriend = {
|
||||||
|
url = "github:synthetic-lab/octofriend";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, octofriend }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
lib = pkgs.lib;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.default = pkgs.buildNpmPackage {
|
||||||
|
pname = "octofriend";
|
||||||
|
version = "git-${builtins.substring 0 7 (octofriend.rev or "unknown")}";
|
||||||
|
|
||||||
|
src = octofriend;
|
||||||
|
|
||||||
|
# First build will fail telling you the correct value. Paste it here.
|
||||||
|
npmDepsHash = "sha256-luAdTozvb0MTuh+xa5qhZPH8YflF9R4GtREAaR8m6Y8=";
|
||||||
|
|
||||||
|
# Ensure a modern node (octofriend is a Node CLI). :contentReference[oaicite:1]{index=1}
|
||||||
|
nodejs = pkgs.nodejs_22;
|
||||||
|
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
|
||||||
|
# If octofriend shells out to git/ssh/etc, keep PATH sane.
|
||||||
|
postInstall = ''
|
||||||
|
if [ -x "$out/bin/octofriend" ]; then
|
||||||
|
wrapProgram "$out/bin/octofriend" \
|
||||||
|
--prefix PATH : ${lib.makeBinPath [ pkgs.git pkgs.openssh ]} \
|
||||||
|
--set OCTOFRIEND_CONFIG_DIR "$out/share/octofriend/config"
|
||||||
|
fi
|
||||||
|
# Provide the short alias if upstream didn't already.
|
||||||
|
if [ -x "$out/bin/octofriend" ] && [ ! -e "$out/bin/octo" ]; then
|
||||||
|
ln -s "$out/bin/octofriend" "$out/bin/octo"
|
||||||
|
fi
|
||||||
|
# Install the config file
|
||||||
|
mkdir -p "$out/share/octofriend/config"
|
||||||
|
cat << 'EOF' > "$out/share/octofriend/config/octofriend.json5"
|
||||||
|
{
|
||||||
|
yourName: 'Jules',
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
model: 'hf:zai-org/GLM-4.6',
|
||||||
|
nickname: 'GLM-4.6 (Synthetic)',
|
||||||
|
context: 131072,
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'hf:MiniMaxAI/MiniMax-M2',
|
||||||
|
nickname: 'MiniMax M2 (Synthetic)',
|
||||||
|
context: 98304,
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultApiKeyOverrides: {},
|
||||||
|
diffApply: {
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
model: 'hf:syntheticlab/diff-apply',
|
||||||
|
},
|
||||||
|
fixJson: {
|
||||||
|
baseUrl: 'https://api.synthetic.new/v1',
|
||||||
|
model: 'hf:syntheticlab/fix-json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Octofriend CLI coding assistant";
|
||||||
|
homepage = "https://github.com/synthetic-lab/octofriend";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "octofriend";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
apps.default = flake-utils.lib.mkApp {
|
||||||
|
drv = self.packages.${system}.default;
|
||||||
|
exePath = "/bin/octofriend";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
# Context Directory
|
|
||||||
|
|
||||||
This directory contains concise documentation for discrete units of work performed in this repository.
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
Document specific configurations, changes, and procedures in a concise format. Prefer clarity over verbosity - use inline comments in code when the config is self-documenting.
|
|
||||||
|
|
||||||
## Organization
|
|
||||||
|
|
||||||
### File Naming
|
|
||||||
- Use lowercase, hyphenated names
|
|
||||||
- Include dates for time-sensitive updates: `feature-update-YYYY-MM-DD.md`
|
|
||||||
- Use descriptive names for ongoing configs: `service-name.md`
|
|
||||||
|
|
||||||
### File Content
|
|
||||||
Each file should document:
|
|
||||||
- **What** changes were made
|
|
||||||
- **Where** the config lives (file paths)
|
|
||||||
- **How** to build/test/deploy
|
|
||||||
- **Why** the change was made (brief context)
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Service Configuration
|
|
||||||
```markdown
|
|
||||||
# Service Name
|
|
||||||
|
|
||||||
**Config:** `servers/service-name.nix`
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
Brief description of what this service does.
|
|
||||||
|
|
||||||
## Build & Deploy
|
|
||||||
```bash
|
|
||||||
nixos-rebuild build --flake .#system
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
Key points to remember.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Event/Update Documentation
|
|
||||||
```markdown
|
|
||||||
# Feature Update - YYYY-MM-DD
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
- Removed: old-feature
|
|
||||||
- Added: new-feature
|
|
||||||
|
|
||||||
## Resolution
|
|
||||||
How the issue was solved.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
Build commands if relevant.
|
|
||||||
```
|
|
||||||
|
|
||||||
## When to Create Files
|
|
||||||
- Major feature additions to services/desktop configs
|
|
||||||
- Significant refactoring or restructuring
|
|
||||||
- Security updates requiring special handling
|
|
||||||
- Cross-service dependencies
|
|
||||||
- Troubleshooting guides for complex issues
|
|
||||||
|
|
||||||
## When NOT to Create Files
|
|
||||||
- Routine package updates
|
|
||||||
- Self-documenting NixOS configurations
|
|
||||||
- Trivial changes covered by code comments
|
|
||||||
- Temporary debugging (use git commits instead)
|
|
||||||
|
|
||||||
## Related Documentation
|
|
||||||
- `agents.md` - Agent instructions and procedures
|
|
||||||
- `README.md` - Project overview
|
|
||||||
- Test scripts in `tests/`
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# Firefox Extension Update - 2026-02-16
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- Privacy Badger (`jid1-MnnxcxisBPnSXQ@jetpack`)
|
|
||||||
- Facebook Container (`@contain-facebook`)
|
|
||||||
- Multi-account Containers (`@testpilot-containers`)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Bitwarden (`{446900e4-71c2-419f-a6a7-df9c2b2dc922}`)
|
|
||||||
- Plasma Integration (`plasma-browser-integration@kde.org`)
|
|
||||||
- MetaMask (`webextension@metamask.io`)
|
|
||||||
- Kagi Search (`kagi-search@kagi.com`)
|
|
||||||
|
|
||||||
### Kept
|
|
||||||
- uBlock Origin (`uBlock0@raymondhill.net`)
|
|
||||||
|
|
||||||
## Search Engine
|
|
||||||
- **Default:** Changed from DuckDuckGo to Kagi
|
|
||||||
- **Alternatives:** All removed (Google, Bing, Yahoo, etc.)
|
|
||||||
|
|
||||||
## Build
|
|
||||||
```bash
|
|
||||||
nixos-rebuild build --flake .#framework
|
|
||||||
sudo nixos-rebuild switch --flake .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manual Setup
|
|
||||||
Sign in after first launch:
|
|
||||||
- Bitwarden account
|
|
||||||
- MetaMask wallet
|
|
||||||
- Kagi account (for full search features)
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
# Firefox Initial Privacy Setup
|
|
||||||
|
|
||||||
**User:** jsutter
|
|
||||||
**Config:** `users/jsutter.nix` → `programs.firefox`
|
|
||||||
|
|
||||||
## Privacy Configuration
|
|
||||||
|
|
||||||
### Privacy Policies (Locked)
|
|
||||||
- Password manager: Disabled
|
|
||||||
- Password saving: Disabled
|
|
||||||
- Form history: Disabled
|
|
||||||
- Telemetry: Disabled
|
|
||||||
- Firefox Studies: Disabled
|
|
||||||
- CaptivePortal: Disabled
|
|
||||||
|
|
||||||
### Homepage & Privacy
|
|
||||||
- Search: Disabled and locked
|
|
||||||
- Top Sites/Highlights/Snippets: Disabled
|
|
||||||
- Recommendations: Disabled (extensions, features)
|
|
||||||
|
|
||||||
### Content Blocking
|
|
||||||
- Mode: Strict
|
|
||||||
- Tracking protection: Enabled (social, fingerprinting, cryptomining)
|
|
||||||
- Do Not Track: Enabled
|
|
||||||
- Fingerprinting resistance: Enabled
|
|
||||||
|
|
||||||
### Permissions (Block All)
|
|
||||||
- Location requests
|
|
||||||
- Notification requests
|
|
||||||
- Autoplay (audio/video)
|
|
||||||
- Virtual Reality requests
|
|
||||||
|
|
||||||
### Data Collection
|
|
||||||
- Sanitize on shutdown: Enabled (cache, cookies, history, etc.)
|
|
||||||
- Private attribution: Disabled
|
|
||||||
- Battery API: Disabled
|
|
||||||
|
|
||||||
## Build & Verify
|
|
||||||
```bash
|
|
||||||
nixos-rebuild build --flake .#framework
|
|
||||||
sudo nixos-rebuild switch --flake .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verification URLs
|
|
||||||
- `about:policies` - Active policies
|
|
||||||
- `about:preferences#privacy` - Privacy settings
|
|
||||||
- `about:preferences#home` - Homepage/new tab settings
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- Original extensions: uBlock Origin, Privacy Badger, Facebook Container, Multi-account Containers
|
|
||||||
- All privacy settings declaratively managed via Home Manager
|
|
||||||
- Settings persist across Firefox updates
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
# Firefox Configuration
|
|
||||||
|
|
||||||
**User:** jsutter
|
|
||||||
**Config:** `users/jsutter.nix` → `programs.firefox`
|
|
||||||
|
|
||||||
## Extensions (Force Installed)
|
|
||||||
- **uBlock Origin** - Ad blocker
|
|
||||||
- **Bitwarden** - Password manager
|
|
||||||
- **Plasma Integration** - KDE integration
|
|
||||||
- **MetaMask** - Web3 wallet
|
|
||||||
- **Kagi Search** - Default/only search engine
|
|
||||||
|
|
||||||
## Key Settings
|
|
||||||
- **Search:** Kagi only (all other engines removed)
|
|
||||||
- **Homepage:** Blank page (`about:blank`)
|
|
||||||
- **Privacy:** Strict content blocking, Do Not Track, fingerprinting resistance
|
|
||||||
- **Telemetry:** Disabled
|
|
||||||
- **Permissions:** Block location/notifications/autoplay/VR requests
|
|
||||||
- **Tabs:** Ctrl+Tab cycles in recent order, hover previews disabled
|
|
||||||
- **Performance:** Hardware acceleration enabled, smooth scrolling via `MOZ_USE_XINPUT2=1`
|
|
||||||
|
|
||||||
## Build & Test
|
|
||||||
```bash
|
|
||||||
nixos-rebuild build --flake .#framework
|
|
||||||
./tests/test-firefox-config.sh
|
|
||||||
sudo nixos-rebuild switch --flake .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manual Setup Required
|
|
||||||
- Sign in to Bitwarden
|
|
||||||
- Sign in to MetaMask
|
|
||||||
- Sign in to Kagi (for full features)
|
|
||||||
- Set zoom per device in `about:config`:
|
|
||||||
- Framework: `layout.css.devPixelsPerPx = 1.1` (110%)
|
|
||||||
- Aurora: `layout.css.devPixelsPerPx = 1.2` (120%)
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
- `about:policies` - Check policies are active
|
|
||||||
- `about:preferences#privacy` - Verify privacy settings
|
|
||||||
- Extensions auto-install on first Firefox launch
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- Extensions cannot be uninstalled (force installed via policy)
|
|
||||||
- Settings are declarative and persist across updates
|
|
||||||
- Title bar hiding requires manual UserChrome.css if desired
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{ config, pkgs, pkgs-unstable, lib, ... }:
|
{ config, pkgs, pkgs-unstable, lib, octofriend, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
(python3.withPackages(ps: with ps; [ pandas requests python-dotenv pip uv ]))
|
(python3.withPackages(ps: with ps; [ pandas requests python-dotenv pip uv ]))
|
||||||
nodejs
|
nodejs
|
||||||
putty # SSH/Telnet client
|
putty # SSH/Telnet client
|
||||||
# octofriend.packages.${pkgs.system}.default
|
octofriend.packages.${pkgs.system}.default
|
||||||
];
|
];
|
||||||
|
|
||||||
programs.nix-ld.enable = true;
|
programs.nix-ld.enable = true;
|
||||||
|
|
||||||
|
|
|
||||||
6
flake.lock
generated
6
flake.lock
generated
|
|
@ -39,11 +39,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771208521,
|
"lastModified": 1770136044,
|
||||||
"narHash": "sha256-X01Q3DgSpjeBpapoGA4rzKOn25qdKxbPnxHeMLNoHTU=",
|
"narHash": "sha256-tlFqNG/uzz2++aAmn4v8J0vAkV3z7XngeIIB3rM3650=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "fa56d7d6de78f5a7f997b0ea2bc6efd5868ad9e8",
|
"rev": "e576e3c9cf9bad747afcddd9e34f51d18c855b4e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,6 @@
|
||||||
./systems/warp.nix
|
./systems/warp.nix
|
||||||
./servers/nginx.nix
|
./servers/nginx.nix
|
||||||
./servers/forgejo.nix
|
./servers/forgejo.nix
|
||||||
./servers/immich.nix
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
skip = mkSystem {
|
skip = mkSystem {
|
||||||
|
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
{ lib, pkgs, config, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.services.immich;
|
|
||||||
|
|
||||||
# External domain for Immich
|
|
||||||
fqdn = "photos.symbiotrip.com";
|
|
||||||
|
|
||||||
# Immich service port (default)
|
|
||||||
immichPort = 2283;
|
|
||||||
|
|
||||||
# Immich media storage location
|
|
||||||
mediaLocation = "/var/lib/immich/media";
|
|
||||||
|
|
||||||
# Borg backup repository path
|
|
||||||
borgRepo = "/var/backups/immich-borg";
|
|
||||||
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
# DNS-based ACME certificate configuration
|
|
||||||
# Uses defaults (dnsProvider, email, credentials) from nginx.nix
|
|
||||||
security.acme.certs.${fqdn} = {
|
|
||||||
# Group needs to be set so nginx can read the certificate
|
|
||||||
group = config.services.nginx.group;
|
|
||||||
|
|
||||||
# Inherit DNS challenge configuration from security.acme.defaults (set in nginx.nix)
|
|
||||||
# This includes: dnsProvider = "cloudflare", environmentFile with Cloudflare token, email
|
|
||||||
|
|
||||||
# Explicitly ensure DNS mode (not HTTP-01)
|
|
||||||
webroot = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Nginx reverse proxy for Immich
|
|
||||||
services.nginx.virtualHosts.${fqdn} = {
|
|
||||||
# Use DNS-based ACME certificate instead of HTTP-01 challenge
|
|
||||||
enableACME = false;
|
|
||||||
useACMEHost = fqdn;
|
|
||||||
forceSSL = true;
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
# Large upload limit for photos and videos (50GB max)
|
|
||||||
client_max_body_size 50000M;
|
|
||||||
|
|
||||||
# Proxy headers
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
|
|
||||||
# Extended timeouts for large file uploads
|
|
||||||
client_body_timeout 600s;
|
|
||||||
client_header_timeout 600s;
|
|
||||||
proxy_read_timeout 600s;
|
|
||||||
proxy_send_timeout 600s;
|
|
||||||
send_timeout 600s;
|
|
||||||
|
|
||||||
# Logging for debugging
|
|
||||||
access_log /var/log/nginx/${fqdn}-access.log;
|
|
||||||
error_log /var/log/nginx/${fqdn}-error.log warn;
|
|
||||||
'';
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:${toString immichPort}";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
recommendedProxySettings = true;
|
|
||||||
|
|
||||||
# Extra websocket configuration
|
|
||||||
extraConfig = ''
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Immich service configuration (NixOS native)
|
|
||||||
services.immich = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
# Bind to localhost only - nginx handles public HTTPS
|
|
||||||
host = "127.0.0.1";
|
|
||||||
port = immichPort;
|
|
||||||
|
|
||||||
# Media storage location
|
|
||||||
mediaLocation = mediaLocation;
|
|
||||||
|
|
||||||
# Firewall - nginx handles external access
|
|
||||||
openFirewall = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Hardware acceleration for video transcoding
|
|
||||||
# This leverages Intel Quick Sync Video on the NUC's Intel GPU
|
|
||||||
services.immich.accelerationDevices = null;
|
|
||||||
|
|
||||||
# Enable graphics acceleration
|
|
||||||
hardware.graphics = {
|
|
||||||
enable = true;
|
|
||||||
enable32Bit = true;
|
|
||||||
extraPackages = with pkgs; [
|
|
||||||
intel-media-driver # Intel Quick Sync Video support
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Add immich user to video and render groups for GPU access
|
|
||||||
users.users.immich.extraGroups = [ "video" "render" ];
|
|
||||||
|
|
||||||
# Ensure nginx starts after ACME certificate is available
|
|
||||||
systemd.services.nginx = {
|
|
||||||
after = [ "acme-${fqdn}.service" "acme-${fqdn}-renew.service" ];
|
|
||||||
requires = [ "acme-${fqdn}.service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# PostgreSQL database backups for Immich
|
|
||||||
services.postgresqlBackup = {
|
|
||||||
enable = true;
|
|
||||||
databases = [ "immich" ];
|
|
||||||
location = "/var/backups/postgresql/immich";
|
|
||||||
startAt = "daily";
|
|
||||||
compression = "zstd";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Borgbackup for Immich media files
|
|
||||||
# Manual repository initialization required:
|
|
||||||
# sudo -u borg borg init --encryption=repokey-blake2 ${borgRepo}
|
|
||||||
services.borgbackup.jobs."immich" = {
|
|
||||||
paths = [ mediaLocation ];
|
|
||||||
repo = borgRepo;
|
|
||||||
|
|
||||||
# Run backup daily at 3 AM
|
|
||||||
startAt = "*-*-* 03:00:00";
|
|
||||||
|
|
||||||
compression = "auto,zstd,6";
|
|
||||||
encryption.mode = "none"; # Repository handles encryption
|
|
||||||
|
|
||||||
prune = {
|
|
||||||
keep = {
|
|
||||||
within = "1d"; # Keep all backups from the last day
|
|
||||||
daily = 7; # Keep last 7 daily backups
|
|
||||||
weekly = 4; # Keep last 4 weekly backups
|
|
||||||
monthly = 6; # Keep last 6 monthly backups
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Note: borgbackup service handles directory creation automatically
|
|
||||||
|
|
||||||
# Create Immich media directory with proper permissions
|
|
||||||
system.activationScripts.immich-media-dir = ''
|
|
||||||
mkdir -p ${mediaLocation}
|
|
||||||
chown -R immich:immich ${mediaLocation}
|
|
||||||
chmod 755 ${mediaLocation}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Systemd service to monitor disk usage
|
|
||||||
services.prometheus.exporters.node = {
|
|
||||||
enable = true;
|
|
||||||
enabledCollectors = [ "systemd" "textfile" "filesystem" ];
|
|
||||||
port = 9100;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
|
||||||
services.pipewire = {
|
services.pipewire = {
|
||||||
enable = true;
|
enable = true;
|
||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
# Tests Directory
|
|
||||||
|
|
||||||
This directory contains test scripts for verifying NixOS configurations.
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
Automated verification of system configurations to catch issues before deployment.
|
|
||||||
|
|
||||||
## Available Tests
|
|
||||||
|
|
||||||
### Firefox Configuration Test
|
|
||||||
**Script:** `test-firefox-config.sh`
|
|
||||||
|
|
||||||
Verifies Firefox Home Manager configuration for user `jsutter`:
|
|
||||||
- Environment variables (`MOZ_USE_XINPUT2`)
|
|
||||||
- Policy file existence and validity
|
|
||||||
- Extension policies
|
|
||||||
- Profile preferences
|
|
||||||
- Privacy settings
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
```bash
|
|
||||||
./test-firefox-config.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
**Requirements:**
|
|
||||||
- Firefox installed
|
|
||||||
- Valid Home Manager configuration
|
|
||||||
- JSON parsing tool (`jq` or Python)
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
|
|
||||||
### All Tests
|
|
||||||
```bash
|
|
||||||
tests/test-firefox-config.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Specific Test
|
|
||||||
```bash
|
|
||||||
./tests/test-firefox-config.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Conventions
|
|
||||||
|
|
||||||
### Shell Scripts
|
|
||||||
- Use `set -e` for error handling
|
|
||||||
- Define colored output for readability
|
|
||||||
- Track pass/fail/skip counts
|
|
||||||
- Return non-zero on failure
|
|
||||||
- Use helper functions (`print_pass`, `print_fail`, etc.)
|
|
||||||
|
|
||||||
### Test Coverage
|
|
||||||
Tests should verify:
|
|
||||||
- Configuration builds successfully
|
|
||||||
- Policies are active (`about:policies`)
|
|
||||||
- Settings are enforced
|
|
||||||
- Extensions are force-installed
|
|
||||||
- User preferences are applied
|
|
||||||
|
|
||||||
## Adding New Tests
|
|
||||||
|
|
||||||
1. Create test script in `tests/`
|
|
||||||
2. Make executable: `chmod +x tests/new-test.sh`
|
|
||||||
3. Document in this README
|
|
||||||
4. Follow naming convention: `test-<service>.sh`
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Many tests require services to be deployed first
|
|
||||||
- Some Firefox tests require Firefox to have been run once
|
|
||||||
- Automated tests complement manual verification
|
|
||||||
- Update test scripts when configurations change
|
|
||||||
|
|
||||||
## Related Documentation
|
|
||||||
|
|
||||||
- `context/` - Configuration documentation
|
|
||||||
- `agents.md` - Agent procedures
|
|
||||||
|
|
@ -1,477 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Firefox Configuration Test Script
|
|
||||||
# Tests the Firefox Home Manager configuration for user jsutter
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Colors for output
|
|
||||||
RED='\033[0;31m'
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
BLUE='\033[0;34m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
# Test counters
|
|
||||||
PASSED=0
|
|
||||||
FAILED=0
|
|
||||||
SKIPPED=0
|
|
||||||
TOTAL=0
|
|
||||||
|
|
||||||
# Helper functions
|
|
||||||
print_header() {
|
|
||||||
echo ""
|
|
||||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
||||||
echo -e "${BLUE} $1${NC}"
|
|
||||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_test() {
|
|
||||||
echo -e "\n${YELLOW}[TEST]${NC} $1"
|
|
||||||
((TOTAL++))
|
|
||||||
}
|
|
||||||
|
|
||||||
print_pass() {
|
|
||||||
echo -e "${GREEN} ✓ PASS${NC}: $1"
|
|
||||||
((PASSED++))
|
|
||||||
}
|
|
||||||
|
|
||||||
print_fail() {
|
|
||||||
echo -e "${RED} ✗ FAIL${NC}: $1"
|
|
||||||
((FAILED++))
|
|
||||||
}
|
|
||||||
|
|
||||||
print_skip() {
|
|
||||||
echo -e "${YELLOW} ⊘ SKIP${NC}: $1"
|
|
||||||
((SKIPPED++))
|
|
||||||
}
|
|
||||||
|
|
||||||
print_info() {
|
|
||||||
echo -e " ℹ ${NC}$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_report() {
|
|
||||||
local description="$1"
|
|
||||||
local pass_count="$2"
|
|
||||||
local fail_count="$3"
|
|
||||||
|
|
||||||
if [[ $fail_count -eq 0 ]]; then
|
|
||||||
print_pass "$description (passed $pass_count/$pass_count)"
|
|
||||||
else
|
|
||||||
local total=$((pass_count + fail_count))
|
|
||||||
print_fail "$description (passed $pass_count/$total)"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if a command exists
|
|
||||||
command_exists() {
|
|
||||||
command -v "$1" &> /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if a preference matches expected value
|
|
||||||
check_pref() {
|
|
||||||
local profile="$1"
|
|
||||||
local pref="$2"
|
|
||||||
local expected="$3"
|
|
||||||
local actual=$(grep "\"${pref}\"" "$profile/prefs.js" 2>/dev/null | head -1 | sed 's/.*user_pref[^,]*,\s*//;s/);.*$//' 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
if [[ "$actual" == *"$expected"* ]]; then
|
|
||||||
print_pass "$pref = $expected"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
print_fail "$pref = $expected (got: $actual)"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get Firefox profile directory
|
|
||||||
get_firefox_profile() {
|
|
||||||
local firefox_dir="$HOME/.mozilla/firefox"
|
|
||||||
|
|
||||||
if [[ -d "$firefox_dir" ]]; then
|
|
||||||
local profile=$(grep -E "Default=1" "$firefox_dir/profiles.ini" 2>/dev/null | head -1 | cut -d= -f2 -s)
|
|
||||||
if [[ -n "$profile" ]] && [[ -d "$firefox_dir/$profile" ]]; then
|
|
||||||
echo "$firefox_dir/$profile"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Try to find jsutter profile
|
|
||||||
profile=$(find "$firefox_dir" -maxdepth 1 -type d -name "*jsutter*" 2>/dev/null | head -1)
|
|
||||||
if [[ -n "$profile" ]]; then
|
|
||||||
echo "$profile"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get first available profile
|
|
||||||
profile=$(ls -1 "$firefox_dir"/*.default* 2>/dev/null | head -1)
|
|
||||||
if [[ -n "$profile" ]]; then
|
|
||||||
echo "$profile"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get Firefox executable
|
|
||||||
get_firefox_exec() {
|
|
||||||
if command_exists firefox-esr &>/dev/null; then
|
|
||||||
echo firefox-esr
|
|
||||||
elif command_exists firefox &>/dev/null; then
|
|
||||||
echo firefox
|
|
||||||
elif [[ -f "/run/current-system/sw/bin/firefox" ]]; then
|
|
||||||
echo /run/current-system/sw/bin/firefox
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
print_header "Firefox Configuration Test Script"
|
|
||||||
echo "Testing configuration for user: $(whoami)"
|
|
||||||
echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Environment Variables
|
|
||||||
# ============================================
|
|
||||||
print_header "1. Environment Variables"
|
|
||||||
|
|
||||||
print_test "Check MOZ_USE_XINPUT2 environment variable"
|
|
||||||
if [[ -z "$MOZ_USE_XINPUT2" ]]; then
|
|
||||||
print_fail "MOZ_USE_XINPUT2 not set"
|
|
||||||
print_info "This should be set in Home Manager sessionVariables"
|
|
||||||
else
|
|
||||||
if [[ "$MOZ_USE_XINPUT2" == "1" ]]; then
|
|
||||||
print_pass "MOZ_USE_XINPUT2 = 1"
|
|
||||||
else
|
|
||||||
print_fail "MOZ_USE_XINPUT2 = $MOZ_USE_XINPUT2 (expected: 1)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_test "Check session variables in shell config"
|
|
||||||
if [[ -f "$HOME/.zshrc" ]] || [[ -f "$HOME/.bashrc" ]]; then
|
|
||||||
print_info "Shell config files found"
|
|
||||||
else
|
|
||||||
print_skip "No shell config file found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Firefox Installation
|
|
||||||
# ============================================
|
|
||||||
print_header "2. Firefox Installation"
|
|
||||||
|
|
||||||
FIREFOX=$(get_firefox_exec)
|
|
||||||
if [[ -n "$FIREFOX" ]]; then
|
|
||||||
print_test "Firefox executable found"
|
|
||||||
print_pass "Firefox at: $FIREFOX"
|
|
||||||
|
|
||||||
print_test "Firefox version"
|
|
||||||
VERSION=$($FIREFOX --version 2>/dev/null || echo "unknown")
|
|
||||||
print_pass "Firefox version: $VERSION"
|
|
||||||
else
|
|
||||||
print_fail "Firefox executable not found"
|
|
||||||
print_info "Check if Firefox is installed via nixos-rebuild switch"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Policy Configuration
|
|
||||||
# ============================================
|
|
||||||
print_header "3. Enterprise Policies"
|
|
||||||
|
|
||||||
POLICY_FILE="/run/current-system/sw/lib/firefox/distribution/policies.json"
|
|
||||||
print_test "Check policy file exists"
|
|
||||||
if [[ -f "$POLICY_FILE" ]]; then
|
|
||||||
print_pass "Policy file found: $POLICY_FILE"
|
|
||||||
else
|
|
||||||
print_fail "Policy file not found"
|
|
||||||
print_info "Activate the configuration with: nixos-rebuild switch"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$POLICY_FILE" ]]; then
|
|
||||||
print_test "Check policy file is valid JSON"
|
|
||||||
if jq empty "$POLICY_FILE" 2>/dev/null; then
|
|
||||||
print_pass "Policy file is valid JSON"
|
|
||||||
else
|
|
||||||
print_fail "Policy file is not valid JSON"
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_test "Check extension policies"
|
|
||||||
EXTENSIONS=$(jq -r '.policies.ExtensionSettings | keys[]' "$POLICY_FILE" 2>/dev/null || echo "")
|
|
||||||
EXPECTED_EXTENSIONS=(
|
|
||||||
"uBlock0@raymondhill.net"
|
|
||||||
"{446900e4-71c2-419f-a6a7-df9c2b2dc922}"
|
|
||||||
"plasma-browser-integration@kde.org"
|
|
||||||
"webextension@metamask.io"
|
|
||||||
"kagi-search@kagi.com"
|
|
||||||
)
|
|
||||||
|
|
||||||
all_found=true
|
|
||||||
for ext in "${EXPECTED_EXTENSIONS[@]}"; do
|
|
||||||
if echo "$EXTENSIONS" | grep -q "$ext"; then
|
|
||||||
print_info "Extension policy found: $ext"
|
|
||||||
else
|
|
||||||
print_fail "Extension policy missing: $ext"
|
|
||||||
all_found=false
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if $all_found; then
|
|
||||||
print_pass "All extension policies configured"
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_test "Check privacy settings policies"
|
|
||||||
PRIVACY_OK=true
|
|
||||||
|
|
||||||
if jq -e '.policies.OfferToSaveLogins == false' "$POLICY_FILE" &>/dev/null; then
|
|
||||||
print_info "Password saving disabled"
|
|
||||||
else
|
|
||||||
print_fail "Password saving not disabled"
|
|
||||||
PRIVACY_OK=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.policies.PasswordManagerEnabled == false' "$POLICY_FILE" &>/dev/null; then
|
|
||||||
print_info "Password manager disabled"
|
|
||||||
else
|
|
||||||
print_fail "Password manager not disabled"
|
|
||||||
PRIVACY_OK=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.policies.DisableTelemetry == true' "$POLICY_FILE" &>/dev/null; then
|
|
||||||
print_info "Telemetry disabled"
|
|
||||||
else
|
|
||||||
print_fail "Telemetry not disabled"
|
|
||||||
PRIVACY_OK=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.policies.DisableFirefoxStudies == true' "$POLICY_FILE" &>/dev/null; then
|
|
||||||
print_info "Firefox Studies disabled"
|
|
||||||
else
|
|
||||||
print_fail "Firefox Studies not disabled"
|
|
||||||
PRIVACY_OK=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $PRIVACY_OK; then
|
|
||||||
print_pass "Privacy settings policies configured correctly"
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_test "Check homepage/new tab policies"
|
|
||||||
HOME_OK=true
|
|
||||||
|
|
||||||
if jq -e '.policies.FirefoxHome.Search == false' "$POLICY_FILE" &>/dev/null; then
|
|
||||||
print_info "Search on home page disabled"
|
|
||||||
else
|
|
||||||
print_fail "Search on home page not disabled"
|
|
||||||
HOME_OK=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.policies.FirefoxHome.TopSites == false' "$POLICY_FILE" &>/dev/null; then
|
|
||||||
print_info "Top Sites disabled"
|
|
||||||
else
|
|
||||||
print_fail "Top Sites not disabled"
|
|
||||||
HOME_OK=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if jq -e '.policies.FirefoxHome.Pocket == false' "$POLICY_FILE" &>/dev/null; then
|
|
||||||
print_info "Pocket disabled"
|
|
||||||
else
|
|
||||||
print_fail "Pocket not disabled"
|
|
||||||
HOME_OK=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $HOME_OK; then
|
|
||||||
print_pass "Homepage/new tab policies configured correctly"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# User Profile Configuration
|
|
||||||
# ============================================
|
|
||||||
print_header "4. Profile Configuration"
|
|
||||||
|
|
||||||
PROFILE=$(get_firefox_profile)
|
|
||||||
if [[ -n "$PROFILE" ]]; then
|
|
||||||
print_test "Firefox profile directory found"
|
|
||||||
print_pass "Profile: $PROFILE"
|
|
||||||
|
|
||||||
# Check for user.js
|
|
||||||
USER_JS="$PROFILE/user.js"
|
|
||||||
if [[ -f "$USER_JS" ]]; then
|
|
||||||
print_test "user.js file exists"
|
|
||||||
print_pass "user.js: $USER_JS"
|
|
||||||
|
|
||||||
# Check key settings
|
|
||||||
print_test "Check key user.js preferences"
|
|
||||||
|
|
||||||
cat > /tmp/firefox_check.sh << 'EOF'
|
|
||||||
#!/bin/bash
|
|
||||||
PROFILE="$1"
|
|
||||||
check_pref() {
|
|
||||||
local pref="$1"
|
|
||||||
local expected="$2"
|
|
||||||
local actual=$(grep "\"${pref}\"" "$PROFILE/user.js" 2>/dev/null | head -1 | sed 's/.*user_pref[^,]*,\s*//;s/);.*$//' 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
if [[ "$actual" == *"$expected"* ]]; then
|
|
||||||
echo "PASS: $pref"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "FAIL: $pref (got: $actual)"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_pref "browser.ctrlTab.recentlyUsedOrder" "true"
|
|
||||||
check_pref "browser.tabs.hoverPreview.enabled" "false"
|
|
||||||
check_pref "browser.link.preview.enabled" "false"
|
|
||||||
check_pref "browser.newtabpage.enabled" "false"
|
|
||||||
check_pref "browser.startup.homepage" "about:blank"
|
|
||||||
check_pref "browser.search.suggest.enabled" "false"
|
|
||||||
check_pref "signon.rememberSignons" "false"
|
|
||||||
check_pref "media.videocontrols.picture-in-picture.allow-multiple" "false"
|
|
||||||
check_pref "browser.contentblocking.category" "strict"
|
|
||||||
check_pref "extensions.pocket.enabled" "false"
|
|
||||||
check_pref "privacy.sanitize.sanitizeOnShutdown" "true"
|
|
||||||
check_pref "toolkit.legacyUserProfileCustomizations.stylesheets" "true"
|
|
||||||
check_pref "privacy.userContext.enabled" "true"
|
|
||||||
EOF
|
|
||||||
chmod +x /tmp/firefox_check.sh
|
|
||||||
output=$(/tmp/firefox_check.sh "$PROFILE")
|
|
||||||
|
|
||||||
pass_count=$(echo "$output" | grep -c "PASS" || true)
|
|
||||||
fail_count=$(echo "$output" | grep -c "FAIL" || true)
|
|
||||||
|
|
||||||
echo "$output" | while read line; do
|
|
||||||
if [[ "$line" == "PASS:"* ]]; then
|
|
||||||
print_pass "${line#PASS: }"
|
|
||||||
else
|
|
||||||
print_fail "${line#FAIL: }"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
print_report "user.js key settings" "$pass_count" "$fail_count"
|
|
||||||
else
|
|
||||||
print_fail "user.js not found (Firefox needs to be run once to generate)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for extensions
|
|
||||||
print_test "Check installed extensions"
|
|
||||||
INSTALLED_EXTENSIONS=$(ls -1 "$PROFILE/extensions" 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
if [[ -n "$INSTALLED_EXTENSIONS" ]]; then
|
|
||||||
print_info "Found $(echo "$INSTALLED_EXTENSIONS" | wc -l) extension(s):"
|
|
||||||
echo "$INSTALLED_EXTENSIONS" | while read ext; do
|
|
||||||
print_info " - $ext"
|
|
||||||
done
|
|
||||||
print_pass "Extensions directory exists"
|
|
||||||
else
|
|
||||||
print_skip "No extensions installed yet (start Firefox to install via policy)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
print_fail "Firefox profile not found"
|
|
||||||
print_info "Start Firefox once to create a profile"
|
|
||||||
print_info "Profile location is typically: ~/.mozilla/firefox/*.default*"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Manual Verification Checklist
|
|
||||||
# ============================================
|
|
||||||
print_header "5. Manual Verification Checklist"
|
|
||||||
|
|
||||||
cat << 'MANUAL'
|
|
||||||
|
|
||||||
The following items require manual verification in Firefox:
|
|
||||||
|
|
||||||
[ ] 1. Open Firefox and check all 4 extensions are installed:
|
|
||||||
- uBlock Origin
|
|
||||||
- Privacy Badger
|
|
||||||
- Facebook Container
|
|
||||||
- Multi-account Containers
|
|
||||||
|
|
||||||
[ ] 2. Open about:preferences#privacy and verify:
|
|
||||||
- Content blocking is set to "Strict"
|
|
||||||
- "Clear history when Firefox closes" is checked
|
|
||||||
- "Remember passwords" is unchecked
|
|
||||||
- "Autoplay" is set to "Block audio and video"
|
|
||||||
|
|
||||||
[ ] 3. Open about:policies and verify:
|
|
||||||
- All policies are "Active"
|
|
||||||
- Extensions are listed as "Force Installed"
|
|
||||||
|
|
||||||
[ ] 4. Test tab behavior:
|
|
||||||
- Press Ctrl+Tab and verify it cycles through recent tabs
|
|
||||||
- Hover over a tab and verify no image preview appears
|
|
||||||
|
|
||||||
[ ] 5. Test new tab/homepage:
|
|
||||||
- Open a new tab (Ctrl+T) - should be blank
|
|
||||||
- Open a new window (Ctrl+N) - should be blank
|
|
||||||
- No search bar, shortcuts, or widgets should appear
|
|
||||||
|
|
||||||
[ ] 6. Test search:
|
|
||||||
- Type in the address bar and verify no suggestions appear
|
|
||||||
- Search results should come from Kagi
|
|
||||||
- No other search engines should be available
|
|
||||||
- Sign in to Kagi account via Kagi extension for full features
|
|
||||||
|
|
||||||
[ ] 7. Test smooth scrolling:
|
|
||||||
- Scroll on a long page - should feel smooth and responsive
|
|
||||||
|
|
||||||
[ ] 8. Test permissions:
|
|
||||||
- Visit a site that requests location - should auto-block
|
|
||||||
- Visit a site that requests notifications - should auto-block
|
|
||||||
- Play a video with audio - should auto-block
|
|
||||||
|
|
||||||
MANUAL
|
|
||||||
|
|
||||||
print_info "Manual verification above requires user interaction"
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Troubleshooting Information
|
|
||||||
# ============================================
|
|
||||||
print_header "6. Troubleshooting Information"
|
|
||||||
|
|
||||||
echo "Useful Firefox URLs:"
|
|
||||||
print_info "about:policies - View active policies"
|
|
||||||
print_info "about:preferences - Firefox settings"
|
|
||||||
print_info "about:config - Advanced preferences"
|
|
||||||
print_info "about:support - Troubleshooting information"
|
|
||||||
print_info "about:addons - View/manage extensions"
|
|
||||||
print_info "about:preferences#privacy - Privacy settings"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
print_info "Configuration files:"
|
|
||||||
print_info " Policies: $POLICY_FILE"
|
|
||||||
print_info " Profile: $PROFILE"
|
|
||||||
print_info " user.js: $USER_JS"
|
|
||||||
if [[ -n "$PROFILE" ]]; then
|
|
||||||
print_info " search.json.mozlz4: $PROFILE/search.json.mozlz4"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
print_info "To rebuild the configuration:"
|
|
||||||
print_info " nixos-rebuild switch"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
print_info "To restart Firefox:"
|
|
||||||
print_info " pkill firefox"
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Summary
|
|
||||||
# ============================================
|
|
||||||
print_header "Test Summary"
|
|
||||||
echo "Total tests: $TOTAL"
|
|
||||||
echo -e "${GREEN}Passed: $PASSED${NC}"
|
|
||||||
echo -e "${RED}Failed: $FAILED${NC}"
|
|
||||||
echo -e "${YELLOW}Skipped: $SKIPPED${NC}"
|
|
||||||
|
|
||||||
if [[ $FAILED -eq 0 ]]; then
|
|
||||||
echo ""
|
|
||||||
echo -e "${GREEN}✓ All automated tests passed!${NC}"
|
|
||||||
echo ""
|
|
||||||
echo "Please run Firefox once to install extensions via policy,"
|
|
||||||
echo "then complete the manual verification checklist above."
|
|
||||||
echo ""
|
|
||||||
echo "Note: Kagi search is the default and only search engine."
|
|
||||||
echo " Sign in to your Kagi account via the Kagi extension."
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo -e "${RED}✗ Some tests failed. Review the output above for details.${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
@ -33,7 +33,6 @@
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
OPENAI_API_KEY = "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";
|
OPENAI_API_KEY = "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";
|
||||||
SYNTHETIC_L_API_KEY = "syn_5bfe68ad3826bb7872f32fcf160e959a";
|
SYNTHETIC_L_API_KEY = "syn_5bfe68ad3826bb7872f32fcf160e959a";
|
||||||
MOZ_USE_XINPUT2 = "1";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.git = {
|
programs.git = {
|
||||||
|
|
@ -172,77 +171,16 @@
|
||||||
OfferToSaveLogins = false;
|
OfferToSaveLogins = false;
|
||||||
OfferToSaveLoginsDefault = false;
|
OfferToSaveLoginsDefault = false;
|
||||||
PasswordManagerEnabled = false;
|
PasswordManagerEnabled = false;
|
||||||
DisableFormHistory = true;
|
|
||||||
FirefoxHome = {
|
FirefoxHome = {
|
||||||
Search = false;
|
Search = true;
|
||||||
Pocket = false;
|
Pocket = false;
|
||||||
Snippets = false;
|
Snippets = false;
|
||||||
TopSites = false;
|
TopSites = false;
|
||||||
Highlights = false;
|
Highlights = false;
|
||||||
Locked = {
|
|
||||||
Search = false;
|
|
||||||
Pocket = false;
|
|
||||||
Snippets = false;
|
|
||||||
TopSites = false;
|
|
||||||
Highlights = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
UserMessaging = {
|
UserMessaging = {
|
||||||
ExtensionRecommendations = false;
|
ExtensionRecommendations = false;
|
||||||
FeatureRecommendations = false;
|
|
||||||
SkipOnboarding = true;
|
SkipOnboarding = true;
|
||||||
MoreFromMozilla = false;
|
|
||||||
WhatsNew = false;
|
|
||||||
};
|
|
||||||
FirefoxSuggest = {
|
|
||||||
WebSuggestions = false;
|
|
||||||
SponsoredSuggestions = false;
|
|
||||||
ImprovSuggest = false;
|
|
||||||
Locked = {
|
|
||||||
WebSuggestions = false;
|
|
||||||
SponsoredSuggestions = false;
|
|
||||||
ImprovSuggest = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Permissions = {
|
|
||||||
Location = {
|
|
||||||
BlockNewRequests = true;
|
|
||||||
Locked = true;
|
|
||||||
};
|
|
||||||
Notifications = {
|
|
||||||
BlockNewRequests = true;
|
|
||||||
Locked = true;
|
|
||||||
};
|
|
||||||
Autoplay = {
|
|
||||||
Default = "block-audio-video";
|
|
||||||
Locked = true;
|
|
||||||
};
|
|
||||||
VirtualReality = {
|
|
||||||
BlockNewRequests = true;
|
|
||||||
Locked = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
ExtensionSettings = {
|
|
||||||
"uBlock0@raymondhill.net" = {
|
|
||||||
installation_mode = "force_installed";
|
|
||||||
install_url = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi";
|
|
||||||
};
|
|
||||||
"{446900e4-71c2-419f-a6a7-df9c2b2dc922}" = {
|
|
||||||
installation_mode = "force_installed";
|
|
||||||
install_url = "https://addons.mozilla.org/firefox/downloads/latest/bitwarden-password-manager/latest.xpi";
|
|
||||||
};
|
|
||||||
"plasma-browser-integration@kde.org" = {
|
|
||||||
installation_mode = "force_installed";
|
|
||||||
install_url = "https://addons.mozilla.org/firefox/downloads/latest/plasma-integration/latest.xpi";
|
|
||||||
};
|
|
||||||
"webextension@metamask.io" = {
|
|
||||||
installation_mode = "force_installed";
|
|
||||||
install_url = "https://addons.mozilla.org/firefox/downloads/latest/ether-metamask/latest.xpi";
|
|
||||||
};
|
|
||||||
"kagi-search@kagi.com" = {
|
|
||||||
installation_mode = "force_installed";
|
|
||||||
install_url = "https://addons.mozilla.org/firefox/downloads/latest/kagi-for-firefox/latest.xpi";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -251,106 +189,13 @@
|
||||||
id = 0;
|
id = 0;
|
||||||
name = "jsutter";
|
name = "jsutter";
|
||||||
settings = {
|
settings = {
|
||||||
"browser.ctrlTab.recentlyUsedOrder" = true;
|
"general.smoothScroll" = true;
|
||||||
"browser.urlbar.quicksuggest.enabled" = false;
|
|
||||||
"browser.urlbar.suggest.quicksuggest.sponsored" = false;
|
|
||||||
"browser.urlbar.suggest.quicksuggest.nonsponsored" = false;
|
|
||||||
"browser.link.preview.enabled" = false;
|
|
||||||
"browser.search.suggest.enabled" = false;
|
|
||||||
"browser.search.suggest.enabled.private" = false;
|
|
||||||
"extensions.pocket.enabled" = false;
|
|
||||||
"extensions.pocket.showHome" = false;
|
|
||||||
"browser.contentblocking.category" = "strict";
|
|
||||||
"privacy.sanitize.sanitizeOnShutdown" = true;
|
|
||||||
"privacy.clearOnShutdown.cache" = true;
|
|
||||||
"privacy.clearOnShutdown.cookies" = true;
|
|
||||||
"privacy.clearOnShutdown.downloads" = true;
|
|
||||||
"privacy.clearOnShutdown.formdata" = true;
|
|
||||||
"privacy.clearOnShutdown.history" = true;
|
|
||||||
"privacy.clearOnShutdown.sessions" = true;
|
|
||||||
"signon.rememberSignons" = false;
|
|
||||||
"dom.private-attribution.submission.enabled" = false;
|
|
||||||
"dom.battery.enabled" = false;
|
|
||||||
"browser.uitour.enabled" = false;
|
|
||||||
"browser.urlbar.trimURLs" = true;
|
|
||||||
"layout.css.prefers-color-scheme.content-override" = 0;
|
|
||||||
"browser.tabs.hoverPreview.enabled" = false;
|
|
||||||
"browser.tabs.hoverPreview.showThumbnails" = false;
|
|
||||||
"media.videocontrols.picture-in-picture.allow-multiple" = false;
|
|
||||||
"media.hardware-video-decoding.force-enabled" = true;
|
|
||||||
"widget.gtk.overlay-scrollbars.enabled" = false;
|
|
||||||
"browser.toolbars.bookmarks.visibility" = "newtab";
|
|
||||||
"browser.toolbars.toolbarbuttons.intended.icon-size" = "small";
|
|
||||||
"browser.newtabpage.enabled" = false;
|
|
||||||
"browser.startup.homepage" = "about:blank";
|
|
||||||
"browser.newtabpage.activity-stream.default.sites" = "";
|
|
||||||
"browser.search.region" = "US";
|
|
||||||
"browser.search.isUS" = true;
|
|
||||||
"layers.acceleration.force-enabled" = true;
|
|
||||||
"gfx.webrender.all" = true;
|
|
||||||
"gfx.webrender.enabled" = true;
|
|
||||||
"svg.context-properties.content.enabled" = true;
|
|
||||||
"browser.zoom.full" = true;
|
|
||||||
"ui.key.menuAccessKeyFocuses" = false;
|
|
||||||
};
|
|
||||||
search = {
|
|
||||||
default = "Kagi";
|
|
||||||
engines = {
|
|
||||||
"Kagi" = {
|
|
||||||
urls = [{ template = "https://kagi.com/search?q={searchTerms}"; }];
|
|
||||||
metaData.alias = "@kagi";
|
|
||||||
icon = "https://kagi.com/favicon.ico";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
force = true;
|
|
||||||
privateDefault = "Kagi";
|
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
|
user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
|
||||||
user_pref("full-screen-api.ignore-widgets", true);
|
user_pref("full-screen-api.ignore-widgets", true);
|
||||||
user_pref("media.ffmpeg.vaapi.enabled", true);
|
user_pref("media.ffmpeg.vaapi.enabled", true);
|
||||||
user_pref("media.rdd-vpx.enabled", true);
|
user_pref("media.rdd-vpx.enabled", true);
|
||||||
user_pref("media.av1.enabled", true);
|
|
||||||
user_pref("media.navigator.video.use_rtt", true);
|
|
||||||
user_pref("browser.display.use_document_fonts", 1);
|
|
||||||
user_pref("browser.display.use_system_colors", false);
|
|
||||||
user_pref("browser.menu.showCharacterEncoding", false);
|
|
||||||
user_pref("browser.newtabpage.activity-stream.feeds.section.topstories.options", "");
|
|
||||||
user_pref("browser.newtabpage.activity-stream.feeds.topsites", false);
|
|
||||||
user_pref("browser.newtabpage.activity-stream.section.highlights.includePocket", false);
|
|
||||||
user_pref("browser.newtabpage.activity-stream.section.highlights.includeVisited", false);
|
|
||||||
user_pref("browser.newtabpage.activity-stream.section.highlights.includeBookmarks", false);
|
|
||||||
user_pref("browser.newtabpage.activity-stream.section.highlights.includeDownloads", false);
|
|
||||||
user_pref("browser.newtabpage.activity-stream.section.highlights.includePocket", false);
|
|
||||||
user_pref("app.shield.optoutstudies.enabled", false);
|
|
||||||
user_pref("datareporting.healthreport.uploadEnabled", false);
|
|
||||||
user_pref("datareporting.policy.dataSubmissionEnabled", false);
|
|
||||||
user_pref("experiments.activeExperiment", false);
|
|
||||||
user_pref("experiments.enabled", false);
|
|
||||||
user_pref("experiments.supported", false);
|
|
||||||
user_pref("network.cookie.cookieBehavior", 1);
|
|
||||||
user_pref("network.dns.disableIPv6", true);
|
|
||||||
user_pref("privacy.donottrackheader.enabled", true);
|
|
||||||
user_pref("privacy.resistFingerprinting", true);
|
|
||||||
user_pref("privacy.trackingprotection.enabled", true);
|
|
||||||
user_pref("privacy.trackingprotection.socialtracking.enabled", true);
|
|
||||||
user_pref("privacy.trackingprotection.fingerprinting.enabled", true);
|
|
||||||
user_pref("privacy.trackingprotection.cryptomining.enabled", true);
|
|
||||||
user_pref("privacy.userContext.enabled", true);
|
|
||||||
user_pref("privacy.userContext.longPressBehavior", 2);
|
|
||||||
user_pref("browser.urlbar.groupLabels.enabled", false);
|
|
||||||
user_pref("browser.search.widget.inNavBar", false);
|
|
||||||
user_pref("browser.search.hiddenOneOffs", "Google,Bing,DuckDuckGo,Amazon,Wikipedia,Yahoo,DDG");
|
|
||||||
user_pref("browser.search.separatePrivateDefault.ui.enabled", false);
|
|
||||||
user_pref("browser.search.suggest.enabled", false);
|
|
||||||
user_pref("browser.search.suggest.enabled.private", false);
|
|
||||||
user_pref("browser.search.order", "");
|
|
||||||
user_pref("browser.search.order.1", "");
|
|
||||||
user_pref("browser.search.order.2", "");
|
|
||||||
user_pref("browser.search.order.3", "");
|
|
||||||
user_pref("browser.search.isUS", true);
|
|
||||||
user_pref("browser.search.region", "US");
|
|
||||||
user_pref("browser.uiCustomization.state", "{\"placements\":{\"widget:[addon]bar-open\",\"nav-bar\":[\"back-button\",\"forward-button\",\"stop-reload-button\",\"urlbar-container\",\"downloads-button\",\"ublock0_raymondhill_net-browser-action\",\"_446900e4-71c2-419f-a6a7-df9c2b2dc922-browser-action\",\"plasma-browser-integration_kde_org-browser-action\",\"webextension_metamask_io-browser-action\",\"kagi-search_kagi_com-browser-action\"],\"toolbar-menubar\":[\"menubar-items\"],\"TabsToolbar\":[\"tabbrowser-tabs\",\"new-tab-button\",\"alltabs-button\"],\"PersonalToolbar\":[\"personal-bookmarks\"]},\"seen\":[\"developer-button\",\"ublock0_raymondhill_net-browser-action\",\"_446900e4-71c2-419f-a6a7-df9c2b2dc922-browser-action\",\"plasma-browser-integration_kde_org-browser-action\",\"webextension_metamask_io-browser-action\",\"kagi-search_kagi_com-browser-action\"],\"dirtyAreaCache\":[\"nav-bar\",\"toolbar-menubar\",\"TabsToolbar\",\"PersonalToolbar\"],\"currentVersion\":18,\"newElementState\":{}}");
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue