Files
kyleandornith-1.5-35b-a3b 7e78de633f P5a: digests cite pages; sift_pdf_* metrics; sift fetch --pages/--grep; skill, unit and README
Digest systemPrompt gains a page-cite instruction and PromptVersion is 3.
Add the sift_pdf_* metric series (extract_total{outcome}, extract_seconds,
pages_total, pool_wait_seconds, cache_hits_total) to internal/metrics and wire
ObservePDF/ObservePDFWait/PDFCacheHit into internal/api/pdf.go. Refactor
cmd/sift/fetchcmd.go to a fetchOpts struct with parseFetchOpts and fetchBody,
adding --pages/--grep. Update skill/SKILL.md, set nix MemoryMax to 1G, and add a
PDF documents section to the README.

Co-Authored-By: ornith-1.5-35b-a3b <ornith-1.5-35b-a3b@llama.cpp.invalid>
2026-09-26 04:36:49 -07:00

198 lines
6.2 KiB
Nix

{ config, lib, pkgs, ... }:
let
cfg = config.services.sift;
settingsFormat = pkgs.formats.toml { };
in
{
options.services.sift = {
enable = lib.mkEnableOption "sift LLM-oriented fetch and search";
package = lib.mkOption {
type = lib.types.package;
description = "sift package. The flake module sets this to packages.sift.";
};
listen = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1:8890";
description = "Address the HTTP server binds to (loopback by default).";
};
tailscaleServe = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Advertise the listener on tailscale serve at https://<host>:8443.";
};
tailscaleHttpsPort = lib.mkOption {
type = lib.types.port;
default = 8443;
description = "HTTPS port for tailscale serve (443 is SearXNG on orion).";
};
denyPrefixes = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
Extra CIDRs the SSRF guard blocks at pre-check and connect time.
Put home WAN /32s here (hairpin into LAN). Set on the host; do not
commit residential IPs to the sift repo.
'';
};
selfIP = {
sources = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "trace:https://1.1.1.1/cdn-cgi/trace" ];
description = ''
Discovery sources in order: ddns:<hostname> and/or
trace:<url>. Results that are not global unicast are ignored.
'';
};
interval = lib.mkOption {
type = lib.types.str;
default = "10m";
description = "How often to re-discover the WAN address.";
};
keepPrevious = lib.mkOption {
type = lib.types.str;
default = "24h";
description = "How long to keep a previous WAN address in the deny set.";
};
};
settings = lib.mkOption {
type = settingsFormat.type;
default = { };
description = "Written to /etc/sift/config.toml. Unused keys are reserved for P1+.";
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.package != null;
message = "services.sift.package must be set (the flake module does this).";
}
];
users.users.sift = {
isSystemUser = true;
group = "sift";
home = "/var/lib/sift";
};
users.groups.sift = { };
environment.etc."sift/config.toml".source = settingsFormat.generate "sift.toml" (
{
listen = cfg.listen;
self_ip = {
sources = cfg.selfIP.sources;
interval = cfg.selfIP.interval;
keep_previous = cfg.selfIP.keepPrevious;
path = "/var/lib/sift/self_ip.json";
};
}
// cfg.settings
);
# Keys and env secrets are created on-box, never in the store.
systemd.tmpfiles.rules = [
"d /var/lib/sift 0750 root sift -"
];
system.activationScripts.sift-secrets = {
deps = [ "users" ];
text = ''
install -d -m 0750 -o root -g sift /var/lib/sift
if [ ! -f /var/lib/sift/keys.toml ]; then
umask 027
printf '%s\n' '# key-hash → { name, workspace }. Generated empty; fill on-box.' > /var/lib/sift/keys.toml
chown root:sift /var/lib/sift/keys.toml
chmod 0640 /var/lib/sift/keys.toml
fi
if [ ! -f /var/lib/sift/secret.env ]; then
umask 027
printf '%s\n' '# BRAVE_API_KEY= (set on-box; do not read SearXNG secret.env)' '# TAVILY_API_KEY= (set on-box; never commit)' > /var/lib/sift/secret.env
chown root:sift /var/lib/sift/secret.env
chmod 0640 /var/lib/sift/secret.env
else
if ! grep -q '^#* *TAVILY_API_KEY' /var/lib/sift/secret.env; then
printf '%s\n' '# TAVILY_API_KEY= (set on-box; never commit)' >> /var/lib/sift/secret.env
fi
fi
'';
};
systemd.services.sift = {
description = "sift LLM-oriented fetch and search";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = "sift";
Group = "sift";
ExecStart = "${lib.getExe cfg.package} serve -listen ${cfg.listen} -config /etc/sift/config.toml"
+ lib.concatMapStrings (p: " -deny-prefix ${lib.escapeShellArg p}") cfg.denyPrefixes;
WorkingDirectory = "/var/lib/sift";
Restart = "on-failure";
RestartSec = "5s";
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
ProtectHostname = true;
ProtectClock = true;
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
MemoryMax = "1G";
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
];
CapabilityBoundingSet = "";
AmbientCapabilities = "";
ReadWritePaths = [ "/var/lib/sift" ];
ConfigurationDirectory = "sift";
StateDirectory = "sift";
EnvironmentFile = "-/var/lib/sift/secret.env";
};
};
systemd.services.tailscale-serve-sift = lib.mkIf cfg.tailscaleServe {
description = "Advertise sift on Tailscale Serve :${toString cfg.tailscaleHttpsPort}";
after = [
"tailscaled.service"
"network-online.target"
"sift.service"
];
wants = [
"tailscaled.service"
"network-online.target"
];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
Restart = "on-failure";
RestartSec = "5s";
ExecStart = "${lib.getExe pkgs.tailscale} serve --bg --yes --https=${toString cfg.tailscaleHttpsPort} http://${cfg.listen}";
};
};
};
}