Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
Nix
at sl/comment
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222{ config, lib, ...}: let cfg = config.services.tangled.spindle;in with lib; { options = { services.tangled.spindle = { enable = mkOption { type = types.bool; default = false; description = "Enable a tangled spindle"; }; package = mkOption { type = types.package; description = "Package to use for the spindle"; };
server = { listenAddr = mkOption { type = types.str; default = "0.0.0.0:6555"; description = "Address to listen on"; };
dbPath = mkOption { type = types.path; default = "/var/lib/spindle/spindle.db"; description = "Path to the database file"; };
hostname = mkOption { type = types.str; example = "my.spindle.com"; description = "Hostname for the server (required)"; };
plcUrl = mkOption { type = types.str; default = "https://plc.directory"; description = "atproto PLC directory"; };
jetstreamEndpoint = mkOption { type = types.str; default = "wss://jetstream1.us-west.bsky.network/subscribe"; description = "Jetstream endpoint to subscribe to"; };
dev = mkOption { type = types.bool; default = false; description = "Enable development mode (disables signature verification)"; };
owner = mkOption { type = types.str; example = "did:plc:qfpnj4og54vl56wngdriaxug"; description = "DID of owner (required)"; };
maxJobCount = mkOption { type = types.int; default = 2; example = 5; description = "Maximum number of concurrent jobs to run"; };
queueSize = mkOption { type = types.int; default = 100; example = 100; description = "Maximum number of jobs queue up"; };
maxConcurrentWorkflows = mkOption { type = types.int; default = 8; description = "Maximum number of workflow containers running simultaneously (controls total memory usage)"; };
secrets = { provider = mkOption { type = types.str; default = "sqlite"; description = "Backend to use for secret management, valid options are 'sqlite', and 'openbao'."; };
openbao = { proxyAddr = mkOption { type = types.str; default = "http://127.0.0.1:8200"; description = "Address of the OpenBAO proxy server"; }; mount = mkOption { type = types.str; default = "spindle"; description = "Mount path in OpenBAO to read secrets from"; }; }; };
tap = { embed = mkOption { type = types.bool; default = true; description = "Run an embedded tap inside the spindle process"; };
url = mkOption { type = types.str; default = "http://[::1]:2480"; description = "URL the spindle's tap client dials"; };
bind = mkOption { type = types.str; default = "[::1]:2480"; description = "Loopback address the embedded tap server listens on"; };
dbPath = mkOption { type = types.path; default = "/var/lib/spindle/tap.db"; description = "Path to the embedded tap sqlite database"; };
relayUrl = mkOption { type = types.str; default = "https://bsky.network"; description = "Relay used by the embedded tap firehose"; }; }; };
pipelines = { nixery = mkOption { type = types.str; default = "nixery.tangled.sh"; # note: this is *not* on tangled.org yet description = "Nixery instance to use"; };
workflowTimeout = mkOption { type = types.str; default = "5m"; description = "Timeout for each step of a pipeline"; };
maxJobMemoryMb = mkOption { type = types.int; default = 6144; description = "Memory limit per workflow container in MiB (default 6 GiB)"; };
logBucket = mkOption { type = types.str; default = "tangled-logs"; description = "S3 bucket for workflow logs"; }; };
environmentFile = mkOption { type = with types; nullOr path; default = null; example = "/etc/spindle.env"; description = '' Additional environment file as defined in {manpage}`systemd.exec(5)`.
Sensitive secrets such as {env}`AWS_SECRET_ACCESS_KEY`, {env}`AWS_ACCESS_KEY_ID`, {env}`AWS_REGION` may be passed to the service without making them world readable in the nix store. ''; }; }; };
config = mkIf cfg.enable { virtualisation.docker.enable = true;
systemd.services.spindle = { description = "spindle service"; after = ["network.target" "docker.service"]; wantedBy = ["multi-user.target"]; serviceConfig = { LogsDirectory = "spindle"; StateDirectory = "spindle"; EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
Environment = [ "SPINDLE_SERVER_LISTEN_ADDR=${cfg.server.listenAddr}" "SPINDLE_SERVER_DB_PATH=${cfg.server.dbPath}" "SPINDLE_SERVER_HOSTNAME=${cfg.server.hostname}" "SPINDLE_SERVER_PLC_URL=${cfg.server.plcUrl}" "SPINDLE_SERVER_JETSTREAM_ENDPOINT=${cfg.server.jetstreamEndpoint}" "SPINDLE_SERVER_DEV=${lib.boolToString cfg.server.dev}" "SPINDLE_SERVER_OWNER=${cfg.server.owner}" "SPINDLE_SERVER_MAX_JOB_COUNT=${toString cfg.server.maxJobCount}" "SPINDLE_SERVER_QUEUE_SIZE=${toString cfg.server.queueSize}" "SPINDLE_SERVER_MAX_CONCURRENT_WORKFLOWS=${toString cfg.server.maxConcurrentWorkflows}" "SPINDLE_SERVER_SECRETS_PROVIDER=${cfg.server.secrets.provider}" "SPINDLE_SERVER_SECRETS_OPENBAO_PROXY_ADDR=${cfg.server.secrets.openbao.proxyAddr}" "SPINDLE_SERVER_SECRETS_OPENBAO_MOUNT=${cfg.server.secrets.openbao.mount}" "SPINDLE_SERVER_TAP_EMBED=${lib.boolToString cfg.server.tap.embed}" "SPINDLE_SERVER_TAP_URL=${cfg.server.tap.url}" "SPINDLE_SERVER_TAP_BIND=${cfg.server.tap.bind}" "SPINDLE_SERVER_TAP_DB_PATH=${cfg.server.tap.dbPath}" "SPINDLE_SERVER_TAP_RELAY_URL=${cfg.server.tap.relayUrl}" "SPINDLE_NIXERY_PIPELINES_NIXERY=${cfg.pipelines.nixery}" "SPINDLE_NIXERY_PIPELINES_WORKFLOW_TIMEOUT=${cfg.pipelines.workflowTimeout}" "SPINDLE_NIXERY_PIPELINES_MAX_JOB_MEMORY_MB=${toString cfg.pipelines.maxJobMemoryMb}" "SPINDLE_S3_LOG_BUCKET=${cfg.pipelines.logBucket}" ]; ExecStart = "${cfg.package}/bin/spindle"; Restart = "always"; }; }; }; }