docs: add hierarchical AGENTS.md knowledge base

- Update root AGENTS.md with regenerative content (144 lines, telegraphic)
- Add pkgs/AGENTS.md for package registry conventions (33 lines)
- Add docs/AGENTS.md for documentation structure (34 lines)
- All files follow telegraphic style, no redundancy with parent
- Preserves existing modules/home-manager/AGENTS.md

Hierarchy:
  ./AGENTS.md (root)
  ├── pkgs/AGENTS.md
  ├── docs/AGENTS.md
  └── modules/home-manager/AGENTS.md (existing)
This commit is contained in:
m3tm3re
2025-12-30 15:02:58 +01:00
parent c5e161026d
commit 824ad97ef9
12 changed files with 863 additions and 1251 deletions

3
.gitignore vendored
View File

@@ -31,6 +31,3 @@ test-result/
# Local configuration (if you want to keep local overrides)
local.nix
flake.lock.bak
# Documentation
docs/

141
AGENTS.md
View File

@@ -1,14 +1,14 @@
# m3ta-nixpkgs Knowledge Base
**Generated:** 2025-12-29
**Commit:** 9092e6d
**Generated:** 2025-12-30
**Commit:** c5e1610
**Branch:** master
## Overview
## OVERVIEW
Personal Nix flake: custom packages, overlays, NixOS/Home Manager modules, dev shells. Flakes-only (no channels).
## Structure
## STRUCTURE
```
.
@@ -24,7 +24,7 @@ Personal Nix flake: custom packages, overlays, NixOS/Home Manager modules, dev s
└── examples/ # Usage examples
```
## Where to Look
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
@@ -35,23 +35,7 @@ Personal Nix flake: custom packages, overlays, NixOS/Home Manager modules, dev s
| Add dev shell | `shells/<name>.nix` | Register in `shells/default.nix` |
| Use port management | `config.m3ta.ports.get "service"` | Host-specific via `hostOverrides` |
## Commands
```bash
nix flake check # Validate flake
nix fmt # Format (nixpkgs-fmt)
nix build .#<pkg> # Build package
nix flake show # List outputs
nix develop # Enter dev shell
nix develop .#python # Python shell
nix develop .#devops # DevOps shell
# In dev shell only:
statix check . # Lint
deadnix . # Find dead code
```
## Code Style
## CONVENTIONS
**Formatter**: `nix fmt` before commit (nixpkgs-fmt)
@@ -93,80 +77,47 @@ meta = with lib; {
};
```
## Package Patterns
## PACKAGE PATTERNS
**Rust** (code2prompt):
```nix
rustPlatform.buildRustPackage rec {
cargoLock.lockFile = src + "/Cargo.lock";
}
**Rust**: `rustPlatform.buildRustPackage rec { cargoLock.lockFile = src + "/Cargo.lock"; }`
**Shell**: `writeShellScriptBin "name" ''script''` or `mkDerivation` with custom `installPhase`
**AppImage**: `appimageTools.wrapType2 { ... }`
**Custom fetcher**: `fetchFromGitea { domain = "code.m3ta.dev"; owner = "m3tam3re"; ... }`
## MODULE PATTERNS
**Simple**: `options.cli.name = { enable = mkEnableOption "..."; }; config = mkIf cfg.enable { ... };`
**Multiple**: `config = mkMerge [ (mkIf cfg.x.enable { ... }) (mkIf cfg.y.enable { ... }) ];`
**Shared lib**: `portsLib = import ../../lib/ports.nix { inherit lib; }; portHelpers = portsLib.mkPortHelpers { ... };`
## PORT MANAGEMENT
Central port management: `config.m3ta.ports.get "service"` with host-specific via `hostOverrides`
Generated: `/etc/m3ta/ports.json` (NixOS), `~/.config/m3ta/ports.json` (HM)
## COMMANDS
```bash
nix flake check # Validate flake
nix fmt # Format (nixpkgs-fmt)
nix build .#<pkg> # Build package
nix flake show # List outputs
nix develop # Enter dev shell
nix develop .#python # Python shell
nix develop .#devops # DevOps shell
# In dev shell only:
statix check . # Lint
deadnix . # Find dead code
```
**Shell scripts** (launch-webapp):
```nix
writeShellScriptBin "name" ''script''
# Or mkDerivation with custom installPhase
```
**AppImage** (msty-studio):
```nix
appimageTools.wrapType2 { ... }
```
**Custom fetcher** (zellij-ps):
```nix
fetchFromGitea {
domain = "code.m3ta.dev";
owner = "m3tam3re";
...
}
```
## Module Patterns
**Simple enable** (zellij-ps.nix):
```nix
options.cli.zellij-ps = {
enable = mkEnableOption "...";
someOption = mkOption { type = types.str; default = "..."; };
};
config = mkIf cfg.enable { home.packages = [...]; };
```
**Multiple conditionals** (editors.nix):
```nix
config = mkMerge [
(mkIf cfg.neovim.enable { ... })
(mkIf cfg.zed.enable { ... })
(mkIf (cfg.neovim.enable || cfg.zed.enable) { ... })
];
```
**Shared library** (ports modules):
```nix
portsLib = import ../../lib/ports.nix { inherit lib; };
portHelpers = portsLib.mkPortHelpers { ports = cfg.definitions; ... };
```
## Port Management
Central port management with host-specific overrides:
```nix
m3ta.ports = {
enable = true;
definitions = { nginx = 80; grafana = 3000; };
hostOverrides.laptop = { nginx = 8080; };
currentHost = config.networking.hostName; # NixOS auto
};
# Usage
services.nginx.port = config.m3ta.ports.get "nginx";
```
**Generated files**: `/etc/m3ta/ports.json` (NixOS), `~/.config/m3ta/ports.json` (HM)
## Anti-Patterns
## ANTI-PATTERNS
| Don't | Do Instead |
|-------|------------|
@@ -176,7 +127,7 @@ services.nginx.port = config.m3ta.ports.get "nginx";
| Skip meta fields | Include all: description, homepage, license, platforms, mainProgram |
| `with pkgs;` in modules | Explicit `pkgs.package` or `with pkgs; [ ... ]` in lists only |
## Commit Format
## COMMIT FORMAT
```
type: brief description
@@ -184,7 +135,7 @@ type: brief description
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `chore`
## Gotchas
## NOTES
- **Hash fetching**: Use `lib.fakeHash` initially, build to get real hash
- **HM modules**: Category subdirs (`cli/`, `coding/`) have own `default.nix` aggregators

View File

@@ -1,450 +0,0 @@
# Contributing to m3ta-nixpkgs
Thank you for your interest in contributing to m3ta-nixpkgs! This guide will help you get started with adding packages, modules, and improvements to this repository.
## Table of Contents
- [Getting Started](#getting-started)
- [Adding a New Package](#adding-a-new-package)
- [Adding a NixOS Module](#adding-a-nixos-module)
- [Adding a Home Manager Module](#adding-a-home-manager-module)
- [Code Style Guidelines](#code-style-guidelines)
- [Testing Your Changes](#testing-your-changes)
- [Submitting Changes](#submitting-changes)
## Getting Started
### Prerequisites
- Nix with flakes enabled
- Git
- Basic understanding of Nix expressions
### Enable Flakes
Add to `~/.config/nix/nix.conf` or `/etc/nix/nix.conf`:
```
experimental-features = nix-command flakes
```
### Clone and Setup
```bash
git clone https://code.m3ta.dev/m3tam3re/nixpkgs
cd m3ta-nixpkgs
nix develop # Enter development environment
```
## Adding a New Package
### Step 1: Create Package Directory
```bash
mkdir pkgs/my-package
```
### Step 2: Write the Package Expression
Create `pkgs/my-package/default.nix` using the template:
```bash
cp templates/package/default.nix pkgs/my-package/default.nix
```
Edit the file to match your package:
```nix
{
lib,
stdenv,
fetchFromGitHub,
}:
stdenv.mkDerivation rec {
pname = "my-package";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
hash = lib.fakeHash; # Use this initially
};
meta = with lib; {
description = "A short description";
homepage = "https://github.com/owner/repo";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "my-package";
};
}
```
### Step 3: Get the Correct Hash
```bash
nix build .#my-package
# The error message will show the correct hash
# Replace lib.fakeHash with the actual hash
```
### Step 4: Register the Package
Add to `pkgs/default.nix`:
```nix
{pkgs, ...}: {
# ... existing packages ...
my-package = pkgs.callPackage ./my-package {};
}
```
### Step 5: Test the Package
```bash
# Build the package
nix build .#my-package
# Run the package
nix run .#my-package
# Check if it works
./result/bin/my-package --version
```
## Adding a NixOS Module
### Step 1: Create Module File
```bash
cp templates/nixos-module/default.nix modules/nixos/my-module.nix
```
### Step 2: Implement the Module
Edit `modules/nixos/my-module.nix`:
```nix
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.m3ta.myModule;
in {
options.m3ta.myModule = {
enable = mkEnableOption "my custom module";
# Add your options here
};
config = mkIf cfg.enable {
# Add your configuration here
};
}
```
### Step 3: Register the Module
Add to `modules/nixos/default.nix`:
```nix
{
config,
lib,
pkgs,
...
}: {
imports = [
./my-module.nix # Add this line
];
}
```
### Step 4: Test the Module
Create a test configuration or add to your existing NixOS configuration:
```nix
{
inputs.m3ta-nixpkgs.url = "path:/path/to/m3ta-nixpkgs";
outputs = {nixpkgs, m3ta-nixpkgs, ...}: {
nixosConfigurations.test = nixpkgs.lib.nixosSystem {
modules = [
m3ta-nixpkgs.nixosModules.default
{
m3ta.myModule.enable = true;
}
];
};
};
}
```
## Adding a Home Manager Module
### Step 1: Create Module File
```bash
cp templates/home-manager-module/default.nix modules/home-manager/my-module.nix
```
### Step 2: Implement the Module
Edit `modules/home-manager/my-module.nix`:
```nix
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.programs.myProgram;
in {
options.programs.myProgram = {
enable = mkEnableOption "my program";
# Add your options here
};
config = mkIf cfg.enable {
# Add your configuration here
};
}
```
### Step 3: Register the Module
Add to `modules/home-manager/default.nix`:
```nix
{
# ... existing modules ...
myModule = import ./my-module.nix;
}
```
### Step 4: Test the Module
Test with Home Manager:
```nix
{
inputs.m3ta-nixpkgs.url = "path:/path/to/m3ta-nixpkgs";
outputs = {home-manager, m3ta-nixpkgs, ...}: {
homeConfigurations.test = home-manager.lib.homeManagerConfiguration {
modules = [
m3ta-nixpkgs.homeManagerModules.default
{
programs.myProgram.enable = true;
}
];
};
};
}
```
## Code Style Guidelines
### Nix Code Style
1. **Formatting**: Use `nixpkgs-fmt` for consistent formatting
```bash
nix fmt
```
2. **Naming Conventions**:
- Package names: lowercase with hyphens (`my-package`)
- Module options: camelCase (`myModule`)
- Variables: camelCase (`cfg`, `myVar`)
3. **File Structure**:
- One package per directory
- Use `default.nix` for the main expression
- Keep related files together
4. **Comments**:
- Add comments for non-obvious code
- Document complex expressions
- Explain why, not what
### Example Good Practice
```nix
{
lib,
stdenv,
fetchFromGitHub,
# Group related dependencies
# Build tools
cmake,
pkg-config,
# Libraries
openssl,
zlib,
}:
stdenv.mkDerivation rec {
pname = "example";
version = "1.0.0";
src = fetchFromGitHub {
owner = "example";
repo = pname;
rev = "v${version}";
hash = "sha256-...";
};
nativeBuildInputs = [
cmake
pkg-config
];
buildInputs = [
openssl
zlib
];
# Explain non-obvious configuration
cmakeFlags = [
"-DENABLE_FEATURE=ON" # Required for proper functionality
];
meta = with lib; {
description = "Clear, concise description";
homepage = "https://example.com";
license = licenses.mit;
maintainers = with maintainers; [];
platforms = platforms.linux;
mainProgram = "example";
};
}
```
## Testing Your Changes
### 1. Check Flake Validity
```bash
nix flake check
```
### 2. Build All Packages
```bash
nix flake show # See all outputs
nix build .#package-name
```
### 3. Test in a Clean Environment
```bash
# Build without any cached results
nix build .#package-name --rebuild
```
### 4. Test Module Integration
Test modules in a VM:
```bash
nixos-rebuild build-vm --flake .#test-config
```
### 5. Verify Metadata
Check that package metadata is complete:
```bash
nix eval .#packages.x86_64-linux.my-package.meta --json | jq
```
## Submitting Changes
### Before Submitting
- [ ] Code follows style guidelines
- [ ] Package builds successfully
- [ ] Tests pass (if applicable)
- [ ] Documentation is updated
- [ ] Commit messages are clear and descriptive
- [ ] CI workflows pass (check GitHub Actions)
### Commit Message Format
```
type: brief description
Longer explanation if needed.
- Detail 1
- Detail 2
```
Types:
- `feat`: New feature (package, module)
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes
- `refactor`: Code refactoring
- `chore`: Maintenance tasks
### Examples
```
feat: add hyprpaper-random package
Add a random wallpaper selector for Hyprpaper.
Includes systemd timer integration.
```
```
fix: correct msty-studio dependencies
Add missing libGL dependency that caused runtime errors.
```
```
docs: update README with usage instructions
Add detailed instructions for using packages and modules.
```
## Best Practices
### Security
- Never include API keys, passwords, or secrets
- Use `lib.fakeHash` initially, then update with correct hash
- Review dependencies for known vulnerabilities
### Performance
- Use `callPackage` for better caching
- Avoid unnecessary `import` statements
- Use overlays efficiently
### Maintainability
- Keep packages focused and simple
- Document complex logic
- Follow nixpkgs conventions
- Update regularly
## Getting Help
- Check [Nix Pills](https://nixos.org/guides/nix-pills/)
- Read [Nixpkgs Manual](https://nixos.org/manual/nixpkgs/stable/)
- Ask in [NixOS Discourse](https://discourse.nixos.org/)
- Join [NixOS Matrix](https://matrix.to/#/#nix:nixos.org)
## License
By contributing, you agree that your contributions will be licensed under the same license as the project.

View File

@@ -1,230 +0,0 @@
# Quick Start Guide - Port Management Module
Get started with centralized port management in 5 minutes!
## Installation
### Step 1: Add to your flake inputs
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
m3ta-nixpkgs.url = "git+https://code.m3ta.dev/m3tam3re/nixpkgs";
};
}
```
### Step 2: Choose your configuration type
## For NixOS Systems
### Basic Setup
```nix
{
imports = [ inputs.m3ta-nixpkgs.nixosModules.default ];
m3ta.ports = {
enable = true;
definitions = {
nginx = 80;
ssh = 22;
grafana = 3000;
};
hostOverrides = {
laptop = { nginx = 8080; ssh = 2222; };
server = {}; # Uses defaults
};
};
}
```
### Using Ports
```nix
# In any NixOS service configuration:
services.nginx.defaultHTTPListenPort = config.m3ta.ports.get "nginx";
services.openssh.ports = [ (config.m3ta.ports.get "ssh") ];
# Firewall rules:
networking.firewall.allowedTCPPorts = [
(config.m3ta.ports.get "ssh")
(config.m3ta.ports.get "nginx")
];
```
## For Home Manager
### Basic Setup
```nix
{
imports = [ inputs.m3ta-nixpkgs.homeManagerModules.default ];
m3ta.ports = {
enable = true;
definitions = {
vite-dev = 5173;
jupyter = 8888;
local-api = 8000;
};
hostOverrides = {
laptop = { vite-dev = 5174; };
desktop = { jupyter = 9999; };
};
currentHost = "laptop"; # Set your hostname
generateEnvVars = true; # Creates PORT_* environment variables
};
}
```
### Using Ports
```nix
# Shell aliases:
programs.bash.shellAliases = {
dev = "PORT=${toString (config.m3ta.ports.get "vite-dev")} npm run dev";
};
# Environment variables:
home.sessionVariables = {
DEV_URL = "http://localhost:${toString (config.m3ta.ports.get "vite-dev")}";
};
# Config files:
home.file.".config/myapp/config.json".text = builtins.toJSON {
port = config.m3ta.ports.get "vite-dev";
};
```
## Common Patterns
### Multi-Host Configuration
```nix
# shared-ports.nix
{
ports = {
nginx = 80;
ssh = 22;
grafana = 3000;
};
hostOverrides = {
laptop = { nginx = 8080; ssh = 2222; };
server = {};
};
}
```
```nix
# In your flake.nix
let
sharedPorts = import ./shared-ports.nix;
in {
nixosConfigurations.laptop = nixpkgs.lib.nixosSystem {
modules = [
m3ta-nixpkgs.nixosModules.default
{
m3ta.ports = {
enable = true;
definitions = sharedPorts.ports;
hostOverrides = sharedPorts.hostOverrides;
currentHost = "laptop";
};
}
];
};
}
```
### Proxy Configuration
```nix
services.nginx.virtualHosts."example.com" = {
locations."/grafana/" = {
proxyPass = "http://127.0.0.1:${toString (config.m3ta.ports.get "grafana")}";
};
locations."/prometheus/" = {
proxyPass = "http://127.0.0.1:${toString (config.m3ta.ports.get "prometheus")}";
};
};
```
### Container Services
```nix
virtualisation.oci-containers.containers.grafana = {
image = "grafana/grafana:latest";
ports = [
"${toString (config.m3ta.ports.get "grafana")}:3000"
];
};
```
## Available Functions
| Function | Description | Example |
| ----------------------------------------------- | -------------------------- | ----------------------------------------------- |
| `config.m3ta.ports.get "service"` | Get port for current host | `config.m3ta.ports.get "nginx"` |
| `config.m3ta.ports.getForHost "host" "service"` | Get port for specific host | `config.m3ta.ports.getForHost "laptop" "nginx"` |
| `config.m3ta.ports.all` | All ports (merged) | `config.m3ta.ports.all` |
| `config.m3ta.ports.services` | List service names | `config.m3ta.ports.services` |
## Debugging
### View all configured ports
```bash
# NixOS
cat /etc/m3ta/ports.json | jq
# Home Manager
cat ~/.config/m3ta/ports.json | jq
```
### Check what port is being used
```nix
# Add to your config temporarily
environment.etc."debug-ports.txt".text = ''
nginx: ${toString (config.m3ta.ports.get "nginx")}
ssh: ${toString (config.m3ta.ports.get "ssh")}
all: ${builtins.toJSON config.m3ta.ports.all}
'';
```
## Implementation Details
The port management module is implemented across several files:
- `lib/ports.nix`: Core utilities for port management
- `modules/nixos/ports.nix`: NixOS module implementation
- `modules/home-manager/ports.nix`: Home Manager module implementation
For detailed implementation and source code, see these files in the repository.
## Common Issues
**"Service not defined" error**
- Make sure the service is in your `definitions` block
**Wrong port being used**
- Check your `currentHost` matches your actual hostname
- Verify overrides in `/etc/m3ta/ports.json` or `~/.config/m3ta/ports.json`
**Type errors**
- Ports must be integers: `nginx = 80;` not `nginx = "80";`
## Need Help?
Open an issue at: https://code.m3ta.dev/m3tam3re/nixpkgs

491
README.md
View File

@@ -1,62 +1,30 @@
# m3ta-nixpkgs
My personal Nix repository containing custom packages, overlays, NixOS modules, and Home Manager modules.
Personal Nix flake repository: custom packages, overlays, NixOS modules, and Home Manager modules.
## Features
- 🎁 **Custom Packages**: Collection of personal Nix packages
- 🔄 **Overlays**: Package modifications and enhancements
- 🐚 **Development Shells**: Pre-configured environments for Python and DevOps
- 🐚 **Development Shells**: Pre-configured environments (Python, DevOps)
- ⚙️ **NixOS Modules**: System-level configuration modules
- 🏠 **Home Manager Modules**: User-level configuration modules
- 📚 **Library Functions**: Helper utilities for configuration management
- ❄️ **Flakes Only**: Modern Nix flakes support (no channels)
## Repository Structure
## Quick Links
```
m3ta-nixpkgs/
├── flake.nix # Main flake configuration
├── pkgs/ # Custom packages
│ ├── default.nix # Package registry
│ ├── code2prompt/
│ ├── hyprpaper-random/
│ ├── launch-webapp/
│ ├── msty-studio/
│ ├── pomodoro-timer/
│ ├── tuxedo-backlight/
│ └── zellij-ps/
├── shells/ # Development shells
│ ├── default.nix # Shell registry (default, python, devops)
│ ├── python.nix # Python development environment
│ └── devops.nix # DevOps/infrastructure tools
├── overlays/ # Overlays
│ ├── default.nix
│ └── mods/ # Package modifications
│ ├── default.nix
│ └── n8n.nix
├── modules/
│ ├── nixos/ # NixOS modules
│ │ ├── default.nix
│ │ └── ports.nix # Port management module
│ └── home-manager/ # Home Manager modules
│ ├── default.nix
│ ├── ports.nix # Port management module
│ └── zellij-ps.nix
├── lib/ # Library functions
│ ├── default.nix # Library entry point
│ └── ports.nix # Port management utilities
├── examples/ # Usage examples
│ ├── home-manager-standalone.nix
│ ├── nixos-configuration.nix
└── templates/ # Templates for new packages/modules
```
- 📖 [Full Documentation](./docs)
- 🚀 [Quick Start Guide](./docs/QUICKSTART.md)
- 📚 [Architecture](./docs/ARCHITECTURE.md)
- 🤝 [Contributing](./docs/CONTRIBUTING.md)
- 📦 [Packages](./docs/packages/)
- ⚙️ [Modules](./docs/modules/)
- 📖 [Guides](./docs/guides/)
## Usage
## Installation
### Adding to Your Flake
Add this repository to your flake inputs:
### Add to Your Flake
```nix
{
@@ -67,446 +35,22 @@ Add this repository to your flake inputs:
}
```
### Using Packages in NixOS Configuration
#### Method 1: Using the Overlay
```nix
{
nixpkgs.overlays = [
inputs.m3ta-nixpkgs.overlays.default
];
environment.systemPackages = with pkgs; [
code2prompt
hyprpaper-random
msty-studio
# ... any other custom packages
];
}
```
#### Method 2: Direct Package Reference
```nix
{
environment.systemPackages = [
inputs.m3ta-nixpkgs.packages.${pkgs.stdenv.hostPlatform.system}.code2prompt
inputs.m3ta-nixpkgs.packages.${pkgs.stdenv.hostPlatform.system}.hyprpaper-random
];
}
```
### Using in Home Manager
#### With Overlay
```nix
{
nixpkgs.overlays = [
inputs.m3ta-nixpkgs.overlays.default
];
home.packages = with pkgs; [
zellij-ps
pomodoro-timer
];
}
```
#### With Home Manager Modules
```nix
{
imports = [
inputs.m3ta-nixpkgs.homeManagerModules.default
# Or specific modules:
# inputs.m3ta-nixpkgs.homeManagerModules.zellij-ps
];
# Module-specific configuration here
}
```
### Using NixOS Modules
```nix
{
imports = [
inputs.m3ta-nixpkgs.nixosModules.default
# Or specific modules:
# inputs.m3ta-nixpkgs.nixosModules.ports
];
# Configure the ports module (if enabled)
m3ta.ports = {
enable = true;
definitions = {
nginx = 80;
ssh = 22;
};
};
}
```
### Building Packages Directly
### Quick Start
```bash
# Build a specific package
# Build a package
nix build git+https://code.m3ta.dev/m3tam3re/nixpkgs#code2prompt
# Run a package without installing
# Run a package
nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#zellij-ps
# Install to your profile
nix profile install git+https://code.m3ta.dev/m3tam3re/nixpkgs#msty-studio
# List all available packages
nix flake show git+https://code.m3ta.dev/m3tam3re/nixpkgs
```
## Development Shells
## Documentation
This repository provides pre-configured development environments. All shells are accessible via `nix develop`.
### Available Shells
| Shell | Description | Usage |
| --------- | ---------------------------------------------- | ---------------------- |
| `default` | Nix development tools for working on this repo | `nix develop` |
| `python` | Python with common libraries and tools | `nix develop .#python` |
| `devops` | Docker, Kubernetes, Terraform, cloud CLIs | `nix develop .#devops` |
### Quick Start
```bash
# Enter a development environment
nix develop git+https://code.m3ta.dev/m3tam3re/nixpkgs#python
nix develop git+https://code.m3ta.dev/m3tam3re/nixpkgs#devops
# Run a command in a shell without entering it
nix develop git+https://code.m3ta.dev/m3tam3re/nixpkgs#python --command python --version
```
### Using Shells in Home Manager
Add shells to your home-manager configuration for persistent access:
```nix
{
inputs.m3ta-nixpkgs.url = "git+https://code.m3ta.dev/m3tam3re/nixpkgs";
# Make tools globally available
home.packages = with inputs.m3ta-nixpkgs.devShells.${pkgs.stdenv.hostPlatform.system};
python.buildInputs ++ devops.buildInputs;
# Or create aliases
programs.zsh.shellAliases = {
dev-python = "nix develop ${inputs.m3ta-nixpkgs}#python";
dev-devops = "nix develop ${inputs.m3ta-nixpkgs}#devops";
};
}
```
### Using Shells in NixOS
Add shells system-wide:
```nix
{
inputs.m3ta-nixpkgs.url = "git+https://code.m3ta.dev/m3tam3re/nixpkgs";
# Make tools available to all users
environment.systemPackages =
inputs.m3ta-nixpkgs.devShells.${pkgs.stdenv.hostPlatform.system}.python.buildInputs;
# System-wide aliases
environment.shellAliases = {
dev-python = "nix develop ${inputs.m3ta-nixpkgs}#python";
};
}
```
### Project-Specific Usage with direnv
Create `.envrc` in your project directory:
```bash
use flake git+https://code.m3ta.dev/m3tam3re/nixpkgs#python
```
Then run `direnv allow`. The environment activates automatically when you enter the directory!
### Extending Shells for Your Project
Create a `flake.nix` in your project that extends a base shell:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
m3ta-nixpkgs.url = "git+https://code.m3ta.dev/m3tam3re/nixpkgs";
};
outputs = { nixpkgs, m3ta-nixpkgs, ... }: {
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
# Inherit all packages from base Python shell
inputsFrom = [ m3ta-nixpkgs.devShells.x86_64-linux.python ];
# Add project-specific packages
buildInputs = [ nixpkgs.legacyPackages.x86_64-linux.postgresql ];
# Project-specific environment variables
DATABASE_URL = "postgresql://localhost/mydb";
};
};
}
```
### Shell Details
See individual shell files for detailed package lists and configuration:
- **Default Shell**: `shells/default.nix` - Nix development tools
- **Python Shell**: `shells/python.nix` - Python development environment
- **DevOps Shell**: `shells/devops.nix` - Infrastructure and cloud tools
## Development
### Setting Up Development Environment
```bash
# Clone the repository
git clone https://code.m3ta.dev/m3tam3re/nixpkgs
cd m3ta-nixpkgs
# Enter development shell
nix develop
# Check flake validity
nix flake check
# Format code
nix fmt
```
### Adding a New Package
1. Create a new directory under `pkgs/`:
```bash
mkdir pkgs/my-package
```
2. Create `pkgs/my-package/default.nix`:
```nix
{
lib,
stdenv,
fetchFromGitHub,
}:
stdenv.mkDerivation rec {
pname = "my-package";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
meta = with lib; {
description = "Description of my package";
homepage = "https://github.com/owner/repo";
license = licenses.mit;
maintainers = with maintainers; [ ];
platforms = platforms.linux;
};
}
```
3. Add to `pkgs/default.nix`:
```nix
{
# ... existing packages ...
my-package = pkgs.callPackage ./my-package {};
}
```
### Adding a New NixOS Module
1. Create `modules/nixos/my-module.nix`
2. Add import to `modules/nixos/default.nix`
3. Update `flake.nix` to expose it:
```nix
nixosModules = {
default = ./modules/nixos;
my-module = ./modules/nixos/my-module.nix;
};
```
### Adding a New Home Manager Module
1. Create `modules/home-manager/my-module.nix`
2. Add to `modules/home-manager/default.nix`
3. Update `flake.nix` to expose it:
```nix
homeManagerModules = {
default = import ./modules/home-manager;
my-module = import ./modules/home-manager/my-module.nix;
};
```
## Port Management Module
**NEW!** Centrally manage service ports across your NixOS systems and Home Manager configurations with automatic host-specific overrides.
### Features
-**Centralized Configuration**: Define all ports in one place
-**Host-Specific Overrides**: Different ports for different machines (laptop, server, desktop)
-**Type Safety**: Full NixOS/Home Manager type system integration
-**Auto-Generated Files**: JSON exports and environment variables
-**Easy Integration**: Works with any service configuration
-**Conflict Prevention**: Avoid port conflicts across different hosts
### Quick Start - NixOS Module
```nix
{
imports = [ inputs.m3ta-nixpkgs.nixosModules.default ];
m3ta.ports = {
enable = true;
definitions = {
nginx = 80;
grafana = 3000;
ssh = 22;
prometheus = 9090;
};
hostOverrides = {
laptop = {
nginx = 8080; # Use non-privileged port
ssh = 2222;
};
};
# Automatically uses system hostname
currentHost = config.networking.hostName;
};
# Use ports in your configuration
services.nginx.defaultHTTPListenPort = config.m3ta.ports.get "nginx";
services.openssh.ports = [ (config.m3ta.ports.get "ssh") ];
# Firewall with dynamic ports
networking.firewall.allowedTCPPorts = [
(config.m3ta.ports.get "ssh")
(config.m3ta.ports.get "nginx")
];
}
```
### Quick Start - Home Manager Module
```nix
{
imports = [ inputs.m3ta-nixpkgs.homeManagerModules.default ];
m3ta.ports = {
enable = true;
definitions = {
vite-dev = 5173;
jupyter = 8888;
local-api = 8000;
};
hostOverrides = {
laptop = {
vite-dev = 5174;
jupyter = 9999;
};
};
currentHost = "laptop";
# Auto-generate PORT_* environment variables
generateEnvVars = true;
};
# Use ports in your configuration
programs.bash.shellAliases = {
dev = "PORT=${toString (config.m3ta.ports.get "vite-dev")} npm run dev";
jupyter = "jupyter lab --port=${toString (config.m3ta.ports.get "jupyter")}";
};
home.sessionVariables = {
DEV_SERVER = "http://localhost:${toString (config.m3ta.ports.get "vite-dev")}";
};
}
```
### Available Module Functions
- `config.m3ta.ports.get "service"` - Get port for service on current host
- `config.m3ta.ports.getForHost "host" "service"` - Get port for specific host
- `config.m3ta.ports.all` - All ports for current host (merged defaults + overrides)
- `config.m3ta.ports.allForHost "host"` - All ports for specific host
- `config.m3ta.ports.services` - List all defined service names
### Auto-Generated Files
**NixOS**: `/etc/m3ta/ports.json`
**Home Manager**: `~/.config/m3ta/ports.json`
These files contain all port configuration for easy inspection and external tool integration.
### Using the Library Directly
For advanced use cases, you can use the underlying library functions without the module:
```nix
{
outputs = { nixpkgs, m3ta-nixpkgs, ... }: {
nixosConfigurations.laptop = nixpkgs.lib.nixosSystem {
modules = [
({ inputs, system, config, ... }: let
m3taLib = inputs.m3ta-nixpkgs.lib.${system};
myPorts = {
ports = { nginx = 80; grafana = 3000; };
hostPorts = { laptop = { nginx = 8080; }; };
};
portHelpers = m3taLib.ports.mkPortHelpers myPorts;
hostname = config.networking.hostName;
in {
services.nginx.port = portHelpers.getPort "nginx" hostname;
})
];
};
};
}
```
### Documentation
See library documentation:
- `lib/ports.nix` - Library source code with inline documentation
Example configurations:
- `examples/nixos-configuration.nix` - NixOS configuration example
- `examples/home-manager-standalone.nix` - Home Manager configuration example
For detailed usage, module documentation, package references, and contribution guidelines, see the [full documentation](./docs).
## Available Packages
@@ -515,6 +59,7 @@ Example configurations:
| `code2prompt` | Convert code to prompts |
| `hyprpaper-random` | Random wallpaper setter for Hyprpaper |
| `launch-webapp` | Launch web applications |
| `mem0` | AI memory assistant with vector storage |
| `msty-studio` | Msty Studio application |
| `pomodoro-timer` | Pomodoro timer utility |
| `tuxedo-backlight` | Backlight control for Tuxedo laptops |

View File

@@ -58,6 +58,7 @@
default = ./modules/nixos;
# Individual modules for selective imports
ports = ./modules/nixos/ports.nix;
mem0 = ./modules/nixos/mem0.nix;
};
# Home Manager modules - for user-level configuration

View File

@@ -11,6 +11,7 @@
# Add your custom modules here as imports or inline definitions
imports = [
./mem0.nix
./ports.nix
# Example: ./my-service.nix
# Add more module files here as you create them

363
modules/nixos/mem0.nix Normal file
View File

@@ -0,0 +1,363 @@
# NixOS Module for Mem0 REST API Server
#
# This module provides a systemd service for the Mem0 REST API server,
# allowing you to run mem0 as a system service with configurable vector storage.
#
# Usage in your NixOS configuration:
#
# # In your flake.nix or configuration.nix:
# imports = [ inputs.m3ta-nixpkgs.nixosModules.default ];
#
# m3ta.mem0 = {
# enable = true;
# port = 8000;
# host = "127.0.0.1";
#
# # LLM Configuration
# llm = {
# provider = "openai";
# apiKeyFile = "/run/secrets/openai-api-key"; # Use agenix or sops-nix
# model = "gpt-4";
# };
#
# # Vector Storage Configuration
# vectorStore = {
# provider = "qdrant"; # or "chroma", "pinecone", etc.
# config = {
# host = "localhost";
# port = 6333;
# };
# };
#
# # Optional: Environment variables
# environmentFile = "/etc/mem0/environment";
# };
#
# Using with m3ta.ports (recommended):
#
# m3ta.ports = {
# enable = true;
# definitions = { mem0 = 8000; };
# hostOverrides.laptop = { mem0 = 8080; };
# currentHost = config.networking.hostName;
# };
#
# m3ta.mem0 = {
# enable = true;
# port = config.m3ta.ports.get "mem0"; # Automatically uses host-specific port
# };
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.m3ta.mem0;
# Python environment with mem0
pythonEnv = pkgs.python3.withPackages (ps:
with ps; [
cfg.package
]);
# Convert vector store config to environment variables
vectorStoreEnv =
if cfg.vectorStore.provider == "qdrant"
then {
MEM0_VECTOR_PROVIDER = "qdrant";
QDRANT_HOST = cfg.vectorStore.config.host or "localhost";
QDRANT_PORT = toString (cfg.vectorStore.config.port or 6333);
QDRANT_COLLECTION = cfg.vectorStore.config.collection_name or "mem0_memories";
}
else if cfg.vectorStore.provider == "pgvector"
then {
MEM0_VECTOR_PROVIDER = "pgvector";
POSTGRES_HOST = cfg.vectorStore.config.host or "localhost";
POSTGRES_PORT = toString (cfg.vectorStore.config.port or 5432);
POSTGRES_DB = cfg.vectorStore.config.dbname or "postgres";
POSTGRES_USER = cfg.vectorStore.config.user or "postgres";
POSTGRES_PASSWORD = cfg.vectorStore.config.password or "postgres";
POSTGRES_COLLECTION = cfg.vectorStore.config.collection_name or "mem0_memories";
}
else if cfg.vectorStore.provider == "chroma"
then {
MEM0_VECTOR_PROVIDER = "chroma";
CHROMA_HOST = cfg.vectorStore.config.host or "localhost";
CHROMA_PORT = toString (cfg.vectorStore.config.port or 8000);
CHROMA_COLLECTION = cfg.vectorStore.config.collection_name or "mem0_memories";
}
else {};
# Start script that sets up environment and runs the server
startScript = pkgs.writeShellScript "mem0-start" ''
set -e
# Load environment file if specified
${optionalString (cfg.environmentFile != null) ''
if [ -f "${cfg.environmentFile}" ]; then
set -a
source "${cfg.environmentFile}"
set +a
fi
''}
# Load API key from file if specified
${optionalString (cfg.llm.apiKeyFile != null) ''
if [ -f "${cfg.llm.apiKeyFile}" ]; then
export OPENAI_API_KEY="$(cat ${cfg.llm.apiKeyFile})"
fi
''}
# Create state directory
mkdir -p ${cfg.stateDir}
cd ${cfg.stateDir}
# Run the server
exec ${pythonEnv}/bin/mem0-server
'';
in {
options.m3ta.mem0 = {
enable = mkEnableOption "Mem0 REST API server";
package = mkOption {
type = types.package;
default = pkgs.mem0;
defaultText = literalExpression "pkgs.mem0";
description = "The mem0 package to use.";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Host address to bind the server to.";
};
port = mkOption {
type = types.port;
default = 8000;
description = "Port to run the REST API server on.";
};
workers = mkOption {
type = types.int;
default = 1;
description = "Number of worker processes.";
};
logLevel = mkOption {
type = types.enum ["critical" "error" "warning" "info" "debug" "trace"];
default = "info";
description = "Logging level for the server.";
};
stateDir = mkOption {
type = types.path;
default = "/var/lib/mem0";
description = "Directory to store mem0 data and state.";
};
user = mkOption {
type = types.str;
default = "mem0";
description = "User account under which mem0 runs.";
};
group = mkOption {
type = types.str;
default = "mem0";
description = "Group under which mem0 runs.";
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Environment file containing additional configuration.
This file should contain KEY=value pairs, one per line.
Useful for secrets that shouldn't be in the Nix store.
'';
example = "/etc/mem0/environment";
};
# LLM Configuration
llm = {
provider = mkOption {
type = types.enum ["openai" "anthropic" "azure" "groq" "together" "ollama" "litellm"];
default = "openai";
description = "LLM provider to use for memory operations.";
};
model = mkOption {
type = types.str;
default = "gpt-4o-mini";
description = "Model name to use for the LLM.";
};
apiKeyFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to file containing the API key for the LLM provider.
The file should contain only the API key.
This is more secure than putting the key in the Nix store.
'';
example = "/run/secrets/openai-api-key";
};
temperature = mkOption {
type = types.nullOr types.float;
default = null;
description = "Temperature parameter for LLM generation.";
};
maxTokens = mkOption {
type = types.nullOr types.int;
default = null;
description = "Maximum tokens for LLM generation.";
};
extraConfig = mkOption {
type = types.attrs;
default = {};
description = "Additional LLM configuration options.";
example = {
top_p = 1.0;
frequency_penalty = 0.0;
};
};
};
# Vector Store Configuration
vectorStore = {
provider = mkOption {
type = types.enum [
"qdrant"
"chroma"
"pinecone"
"weaviate"
"faiss"
"pgvector"
"redis"
"elasticsearch"
"milvus"
];
default = "qdrant";
description = "Vector database provider to use.";
};
config = mkOption {
type = types.attrs;
default = {};
description = ''
Configuration for the vector store.
The structure depends on the provider.
'';
example = literalExpression ''
{
host = "localhost";
port = 6333;
collection_name = "mem0_memories";
}
'';
};
};
# Embedder Configuration
embedder = {
provider = mkOption {
type = types.nullOr (types.enum ["openai" "huggingface" "ollama" "vertexai"]);
default = null;
description = "Embedding model provider. If null, uses default.";
};
model = mkOption {
type = types.nullOr types.str;
default = null;
description = "Embedding model name to use.";
example = "text-embedding-3-small";
};
config = mkOption {
type = types.attrs;
default = {};
description = "Configuration for the embedder.";
example = {
model = "text-embedding-3-small";
};
};
};
};
config = mkIf cfg.enable {
# Create user and group
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
description = "Mem0 service user";
home = cfg.stateDir;
createHome = true;
};
users.groups.${cfg.group} = {};
# Systemd service
systemd.services.mem0 = {
description = "Mem0 REST API Server";
after = ["network.target"];
wantedBy = ["multi-user.target"];
serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
ExecStart = startScript;
Restart = "on-failure";
RestartSec = "5s";
# Security hardening
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [cfg.stateDir];
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictRealtime = true;
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = false; # Python needs this
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
};
environment =
{
PYTHONUNBUFFERED = "1";
MEM0_HOST = cfg.host;
MEM0_PORT = toString cfg.port;
MEM0_LLM_PROVIDER = cfg.llm.provider;
MEM0_LLM_MODEL = cfg.llm.model;
MEM0_HISTORY_DB_PATH = "${cfg.stateDir}/history.db";
MEM0_WORKERS = toString cfg.workers;
MEM0_LOG_LEVEL = cfg.logLevel;
}
// optionalAttrs (cfg.llm.temperature != null) {
MEM0_LLM_TEMPERATURE = toString cfg.llm.temperature;
}
// optionalAttrs (cfg.llm.extraConfig != {}) {
MEM0_LLM_EXTRA_CONFIG = builtins.toJSON cfg.llm.extraConfig;
}
// optionalAttrs (cfg.embedder.provider != null) {
MEM0_EMBEDDER_PROVIDER = cfg.embedder.provider;
}
// optionalAttrs (cfg.embedder.model != null) {
MEM0_EMBEDDER_MODEL = cfg.embedder.model;
}
// vectorStoreEnv;
};
# Open firewall port if binding to non-localhost
networking.firewall.allowedTCPPorts = mkIf (cfg.host != "127.0.0.1" && cfg.host != "localhost") [cfg.port];
};
}

33
pkgs/AGENTS.md Normal file
View File

@@ -0,0 +1,33 @@
# pkgs/ AGENTS.md
## OVERVIEW
Custom package registry using `callPackage` pattern for flake-wide availability.
## STRUCTURE
- `default.nix`: Central registry (entry point for overlays)
- `code2prompt/`: Rust package
- `hyprpaper-random/`: Bash script
- `launch-webapp/`: Webapp wrapper
- `mem0/`: Python package + custom `server.py`
- `msty-studio/`: AppImage wrapper
- `pomodoro-timer/`: Timer utility
- `tuxedo-backlight/`: Hardware control
- `zellij-ps/`: Gitea-hosted package
## WHERE TO LOOK
- **Register new pkg**: Add entry to `pkgs/default.nix` attribute set
- **Modify pkg**: Edit `pkgs/<name>/default.nix` (version, hash, deps)
- **Check visibility**: `nix flake show` (uses `pkgs/default.nix` via `overlays/default.nix`)
- **Add scripts**: Place alongside `default.nix` in package folder (e.g., `mem0/server.py`)
## CONVENTIONS
- **CallPackage**: Always use `pkgs.callPackage ./dir {}` in registry
- **Dir == Attr**: Package directory name MUST match its registry attribute
- **Path literals**: Reference local assets using `./file` within derivations
- **Self-contained**: Keep all package-specific files in their own directory
## ANTI-PATTERNS
- **Orphaned dirs**: Creating `pkgs/new-pkg/` without updating `pkgs/default.nix`
- **Direct flake imports**: Importing packages in `flake.nix` instead of through the registry
- **Implicit deps**: Not declaring dependencies in the package function arguments
- **Non-derivations**: Placing NixOS/HM modules here (use `modules/` instead)

View File

@@ -4,6 +4,7 @@
code2prompt = pkgs.callPackage ./code2prompt {};
hyprpaper-random = pkgs.callPackage ./hyprpaper-random {};
launch-webapp = pkgs.callPackage ./launch-webapp {};
mem0 = pkgs.callPackage ./mem0 {};
msty-studio = pkgs.callPackage ./msty-studio {};
pomodoro-timer = pkgs.callPackage ./pomodoro-timer {};
tuxedo-backlight = pkgs.callPackage ./tuxedo-backlight {};

99
pkgs/mem0/default.nix Normal file
View File

@@ -0,0 +1,99 @@
{
lib,
python3,
fetchFromGitHub,
}:
python3.pkgs.buildPythonPackage rec {
pname = "mem0ai";
version = "1.0.0";
pyproject = true;
src = fetchFromGitHub {
owner = "mem0ai";
repo = "mem0";
rev = "v${version}";
hash = "sha256-DluzIhwL/GanqtRSMW7ax+OVc2kZjbwQ0lpPCQFnD58=";
};
# Relax Python dependency version constraints
# mem0 has strict version pins that may not match nixpkgs versions
pythonRelaxDeps = true;
build-system = with python3.pkgs; [
hatchling
];
dependencies = with python3.pkgs; [
litellm
qdrant-client
pydantic
openai
posthog
pytz
sqlalchemy
protobuf
uvicorn
fastapi
];
optional-dependencies = with python3.pkgs; {
graph = [
# Note: some graph dependencies may not be available in nixpkgs
# neo4j is available, others will need to be packaged separately
];
vector_stores = [
# chromadb # available in nixpkgs
# pinecone-client # may need packaging
# weaviate-client # may need packaging
# faiss # available as faiss-cpu
psycopg
pymongo
pymysql
redis
elasticsearch
];
llms = [
groq
openai
# together # may need packaging
# litellm # may need packaging
# ollama # may need packaging
# google-generativeai # may need packaging
];
extras = [
boto3
# langchain-community # may need packaging
# sentence-transformers # may need packaging
elasticsearch
# fastembed # may need packaging
];
};
# Skip tests for now since they require additional test dependencies
doCheck = false;
# Disable imports check because mem0 tries to create directories at import time
# which fails in the Nix sandbox (/homeless-shelter)
pythonImportsCheck = [];
postInstall = ''
install -Dm755 ${./server.py} $out/bin/mem0-server
'';
meta = with lib; {
description = "Long-term memory layer for AI agents with REST API support";
longDescription = ''
Mem0 provides a sophisticated memory layer for AI applications, offering:
- Memory management for AI agents (add, search, update, delete)
- REST API server for easy integration
- Support for multiple vector storage backends (Qdrant, Chroma, etc.)
- Graph memory capabilities
- Multi-modal support
- Configurable LLM and embedding models
'';
homepage = "https://github.com/mem0ai/mem0";
changelog = "https://github.com/mem0ai/mem0/releases/tag/v${version}";
license = licenses.asl20;
platforms = platforms.linux;
};
}

301
pkgs/mem0/server.py Normal file
View File

@@ -0,0 +1,301 @@
#!/usr/bin/env python3
"""
Mem0 REST API Server
A FastAPI-based REST server for mem0 memory operations.
"""
import logging
import os
import sys
from typing import Any, Dict, List, Optional
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse, RedirectResponse
from pydantic import BaseModel, Field
from mem0 import Memory
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# Configuration from environment variables
def get_config_from_env() -> Dict[str, Any]:
"""Build mem0 configuration from environment variables."""
config = {"version": "v1.1"}
# Vector store configuration
vector_provider = os.environ.get("MEM0_VECTOR_PROVIDER", "qdrant")
config["vector_store"] = {"provider": vector_provider}
if vector_provider == "qdrant":
config["vector_store"]["config"] = {
"host": os.environ.get("QDRANT_HOST", "localhost"),
"port": int(os.environ.get("QDRANT_PORT", "6333")),
"collection_name": os.environ.get("QDRANT_COLLECTION", "mem0_memories"),
}
elif vector_provider == "pgvector":
config["vector_store"]["config"] = {
"host": os.environ.get("POSTGRES_HOST", "localhost"),
"port": int(os.environ.get("POSTGRES_PORT", "5432")),
"dbname": os.environ.get("POSTGRES_DB", "postgres"),
"user": os.environ.get("POSTGRES_USER", "postgres"),
"password": os.environ.get("POSTGRES_PASSWORD", "postgres"),
"collection_name": os.environ.get("POSTGRES_COLLECTION", "mem0_memories"),
}
elif vector_provider == "chroma":
config["vector_store"]["config"] = {
"host": os.environ.get("CHROMA_HOST", "localhost"),
"port": int(os.environ.get("CHROMA_PORT", "8000")),
"collection_name": os.environ.get("CHROMA_COLLECTION", "mem0_memories"),
}
# LLM configuration
llm_provider = os.environ.get("MEM0_LLM_PROVIDER", "openai")
config["llm"] = {
"provider": llm_provider,
"config": {
"model": os.environ.get("MEM0_LLM_MODEL", "gpt-4o-mini"),
}
}
# Temperature: only include if set (null means use provider default)
temperature = os.environ.get("MEM0_LLM_TEMPERATURE")
if temperature is not None:
config["llm"]["config"]["temperature"] = float(temperature)
# Extra config: merge JSON env var if provided
extra_config_json = os.environ.get("MEM0_LLM_EXTRA_CONFIG")
if extra_config_json:
import json
try:
extra_config = json.loads(extra_config_json)
config["llm"]["config"].update(extra_config)
except json.JSONDecodeError:
logging.warning(f"Failed to parse MEM0_LLM_EXTRA_CONFIG: {extra_config_json}")
# Add API key if available
if llm_provider == "openai":
api_key = os.environ.get("OPENAI_API_KEY")
if api_key:
config["llm"]["config"]["api_key"] = api_key
# Embedder configuration
embedder_provider = os.environ.get("MEM0_EMBEDDER_PROVIDER", "openai")
config["embedder"] = {
"provider": embedder_provider,
}
# Embedder model: only include if provider is set
if embedder_provider:
embedder_config = {}
embedder_model = os.environ.get("MEM0_EMBEDDER_MODEL")
if embedder_model:
embedder_config["model"] = embedder_model
config["embedder"]["config"] = embedder_config
if embedder_provider == "openai":
api_key = os.environ.get("OPENAI_API_KEY")
if api_key:
config["embedder"]["config"]["api_key"] = api_key
# History DB path
history_db_path = os.environ.get("MEM0_HISTORY_DB_PATH", "/var/lib/mem0/history.db")
config["history_db_path"] = history_db_path
return config
# Initialize Memory instance
try:
config = get_config_from_env()
logging.info(f"Initializing mem0 with config: {config}")
# Validate API key is set for OpenAI provider
if config.get("llm", {}).get("provider") == "openai":
if not config.get("llm", {}).get("config", {}).get("api_key"):
logging.error("OPENAI_API_KEY environment variable is required but not set.")
logging.error("Please set OPENAI_API_KEY environment variable or configure apiKeyFile in NixOS module.")
sys.exit(1)
MEMORY_INSTANCE = Memory.from_config(config)
logging.info("Memory instance initialized successfully")
except Exception as e:
logging.error(f"Failed to initialize Memory: {e}")
logging.error("Please check your configuration and ensure all required services are running.")
sys.exit(1)
app = FastAPI(
title="Mem0 REST API",
description="A REST API for managing and searching memories for your AI Agents and Apps.",
version="1.0.0",
)
class Message(BaseModel):
role: str = Field(..., description="Role of the message (user or assistant).")
content: str = Field(..., description="Message content.")
class MemoryCreate(BaseModel):
messages: List[Message] = Field(..., description="List of messages to store.")
user_id: Optional[str] = None
agent_id: Optional[str] = None
run_id: Optional[str] = None
metadata: Optional[Dict[str, Any]] = None
class SearchRequest(BaseModel):
query: str = Field(..., description="Search query.")
user_id: Optional[str] = None
run_id: Optional[str] = None
agent_id: Optional[str] = None
filters: Optional[Dict[str, Any]] = None
@app.get("/", summary="Redirect to documentation", include_in_schema=False)
def home():
"""Redirect to the OpenAPI documentation."""
return RedirectResponse(url="/docs")
@app.get("/health", summary="Health check")
def health():
"""Check if the server is running."""
return {"status": "healthy", "service": "mem0-api"}
@app.post("/configure", summary="Configure Mem0")
def set_config(config: Dict[str, Any]):
"""Set memory configuration."""
global MEMORY_INSTANCE
MEMORY_INSTANCE = Memory.from_config(config)
return {"message": "Configuration set successfully"}
@app.post("/memories", summary="Create memories")
def add_memory(memory_create: MemoryCreate):
"""Store new memories."""
if not any([memory_create.user_id, memory_create.agent_id, memory_create.run_id]):
raise HTTPException(status_code=400, detail="At least one identifier (user_id, agent_id, run_id) is required.")
params = {k: v for k, v in memory_create.model_dump().items() if v is not None and k != "messages"}
try:
response = MEMORY_INSTANCE.add(messages=[m.model_dump() for m in memory_create.messages], **params)
return JSONResponse(content=response)
except Exception as e:
logging.exception("Error in add_memory:")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/memories", summary="Get memories")
def get_all_memories(
user_id: Optional[str] = None,
run_id: Optional[str] = None,
agent_id: Optional[str] = None,
):
"""Retrieve stored memories."""
if not any([user_id, run_id, agent_id]):
raise HTTPException(status_code=400, detail="At least one identifier is required.")
try:
params = {
k: v for k, v in {"user_id": user_id, "run_id": run_id, "agent_id": agent_id}.items() if v is not None
}
return MEMORY_INSTANCE.get_all(**params)
except Exception as e:
logging.exception("Error in get_all_memories:")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/memories/{memory_id}", summary="Get a memory")
def get_memory(memory_id: str):
"""Retrieve a specific memory by ID."""
try:
return MEMORY_INSTANCE.get(memory_id)
except Exception as e:
logging.exception("Error in get_memory:")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/search", summary="Search memories")
def search_memories(search_req: SearchRequest):
"""Search for memories based on a query."""
try:
params = {k: v for k, v in search_req.model_dump().items() if v is not None and k != "query"}
return MEMORY_INSTANCE.search(query=search_req.query, **params)
except Exception as e:
logging.exception("Error in search_memories:")
raise HTTPException(status_code=500, detail=str(e))
@app.put("/memories/{memory_id}", summary="Update a memory")
def update_memory(memory_id: str, updated_memory: Dict[str, Any]):
"""Update an existing memory with new content."""
try:
return MEMORY_INSTANCE.update(memory_id=memory_id, data=updated_memory)
except Exception as e:
logging.exception("Error in update_memory:")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/memories/{memory_id}/history", summary="Get memory history")
def memory_history(memory_id: str):
"""Retrieve memory history."""
try:
return MEMORY_INSTANCE.history(memory_id=memory_id)
except Exception as e:
logging.exception("Error in memory_history:")
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/memories/{memory_id}", summary="Delete a memory")
def delete_memory(memory_id: str):
"""Delete a specific memory by ID."""
try:
MEMORY_INSTANCE.delete(memory_id=memory_id)
return {"message": "Memory deleted successfully"}
except Exception as e:
logging.exception("Error in delete_memory:")
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/memories", summary="Delete all memories")
def delete_all_memories(
user_id: Optional[str] = None,
run_id: Optional[str] = None,
agent_id: Optional[str] = None,
):
"""Delete all memories for a given identifier."""
if not any([user_id, run_id, agent_id]):
raise HTTPException(status_code=400, detail="At least one identifier is required.")
try:
params = {
k: v for k, v in {"user_id": user_id, "run_id": run_id, "agent_id": agent_id}.items() if v is not None
}
MEMORY_INSTANCE.delete_all(**params)
return {"message": "All relevant memories deleted"}
except Exception as e:
logging.exception("Error in delete_all_memories:")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/reset", summary="Reset all memories")
def reset_memory():
"""Completely reset stored memories."""
try:
MEMORY_INSTANCE.reset()
return {"message": "All memories reset"}
except Exception as e:
logging.exception("Error in reset_memory:")
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
host = os.environ.get("MEM0_HOST", "127.0.0.1")
port = int(os.environ.get("MEM0_PORT", "8000"))
workers = int(os.environ.get("MEM0_WORKERS", "1"))
log_level = os.environ.get("MEM0_LOG_LEVEL", "info")
uvicorn.run(app, host=host, port=port, workers=workers, log_level=log_level)