Skip to content

Configuration

SuckerPot is configured with environment variables. You set them in .env, which Docker Compose reads. The template is .env.example. Copy it, then replace every placeholder.

Terminal window
cp .env.example .env
chmod 600 .env

Never commit .env. It holds passwords. The .gitignore already excludes it.

This page lists every variable the code reads today. “In .env” means Compose passes it through from your .env. “Fixed by Compose” means Compose sets it, and you should not change it. A variable that the code reads but Compose does not pass through can only be changed by editing docker-compose.yml (an override file is the clean way).

Compose refuses to start until the required secrets are set. Each secret must be at least 24 characters long, and no two may be equal. The Redis and migration containers check this at start-up and refuse to run otherwise. The message names the variable, never its value.

Terminal window
openssl rand -base64 32
SecretUsed byPurpose
POSTGRES_PASSWORDdb, migrateDatabase owner. Schema changes only.
POSTGRES_INGEST_PASSWORDmigrate, ingestRole suckerpot_ingest
POSTGRES_API_PASSWORDmigrate, apiRole suckerpot_api (read-only)
REDIS_PASSWORDredisRedis admin user (operators only)
REDIS_HEALTH_PASSWORDredisRedis health user. It can only run PING.
REDIS_INGEST_PASSWORDredis, ingestRedis ingest user. It reads and acknowledges the event streams.
REDIS_SENSOR_SSH_PASSWORDredis, sensor-sshRedis sensor-ssh user. It can only XADD to events:ssh.
REDIS_SENSOR_RDP_PASSWORDredis, sensor-rdpRedis sensor-rdp user. It can only XADD to events:rdp.
REDIS_SENSOR_TELNET_PASSWORDredis, sensor-telnetRedis sensor-telnet user. It can only XADD to events:telnet.
REDIS_SENSOR_WEB_PASSWORDredis, sensor-webRedis sensor-web user. It can only XADD to events:web.
REDIS_SENSOR_REDIS_PASSWORDredis, sensor-redisRedis sensor-redis user. It can only XADD to events:redis.
REDIS_SENSOR_DOCKER_PASSWORDredis, sensor-dockerRedis sensor-docker user. It can only XADD to events:docker.
REDIS_SENSOR_MYSQL_PASSWORDredis, sensor-mysqlRedis sensor-mysql user. It can only XADD to events:mysql.
REDIS_SENSOR_MSSQL_PASSWORDredis, sensor-mssqlRedis sensor-mssql user. It can only XADD to events:mssql.
REDIS_SENSOR_ADB_PASSWORDredis, sensor-adbRedis sensor-adb user. It can only XADD to events:adb.
REDIS_SENSOR_SMB_PASSWORDredis, sensor-smbRedis sensor-smb user. It can only XADD to events:smb.
REDIS_SENSOR_SIP_PASSWORDredis, sensor-sipRedis sensor-sip user. It can only XADD to events:sip.
REDIS_SENSOR_SYNOLOGY_PASSWORDredis, sensor-synologyRedis sensor-synology user. It can only XADD to events:synology.
REDIS_DEMO_PASSWORDredis, demoRedis demo user. It can only XADD to events:*.
SESSION_SECRETapiSigns the dashboard session cookie (at least 32 bytes). See Dashboard authentication.
ADMIN_PASSWORD_HASHapiArgon2 hash of the dashboard password. A hash, not a password, but treat it as a secret.
SETTINGS_ENCRYPTION_KEYapi, ingestOptional. Encrypts the enrichment API keys you enter on the Settings page. Make it with openssl rand -base64 32 (at least 32 random bytes). See Enrichment keys in Settings.

Each of the twelve sensors has its own Redis secret, REDIS_SENSOR_<NAME>_PASSWORD, for SSH, Telnet, RDP, web, Redis, Docker, MySQL, MSSQL, ADB, SMB, SIP and Synology. To rotate secrets, see Deployment.

VariableDefaultSourceDescription
COMPOSE_PROJECT_NAMEsuckerpot.envPrefix for container, network and volume names
DASHBOARD_BIND127.0.0.1.envHost address the dashboard port binds to. Keep 127.0.0.1 unless a tunnel on another machine must reach it, and then filter the port upstream.
DASHBOARD_PORT8443.envHost port for the dashboard. Never 22, 23, 80, 443, 3389 or the admin SSH port.
ADMIN_SSH_PORTempty.envOptional. The port of the host’s own admin SSH daemon. Read only by scripts/check-ports.sh, to refuse a collision.
SSH_HOST_PORT22.envHost port of the SSH sensor (IPv4 only)
TELNET_HOST_PORT23.envHost port of the Telnet sensor
HTTP_HOST_PORT80.envHost port of the web sensor (IPv4 only)
HTTPS_HOST_PORT443.envHost port of the web sensor’s TLS listener (IPv4 only)
RDP_HOST_PORT3389.envHost port of the RDP sensor
REDIS_HOST_PORT6379.envHost port of the Redis sensor (IPv4 only)
DOCKER_HOST_PORT2375.envHost port of the Docker API sensor (IPv4 only)
MYSQL_HOST_PORT3306.envHost port of the MySQL sensor (IPv4 only)
MSSQL_HOST_PORT1433.envHost port of the MSSQL sensor (IPv4 only)
ADB_HOST_PORT5555.envHost port of the ADB sensor (IPv4 only)
SMB_HOST_PORT445.envHost port of the SMB sensor (IPv4 only). The sensor listens on container port 4450.
SIP_HOST_PORT5060.envHost port of the SIP sensor, for both UDP and TCP (IPv4 only)
SYNOLOGY_HTTP_HOST_PORT5000.envHost port of the Synology sensor’s HTTP listener (IPv4 only)
SYNOLOGY_HTTPS_HOST_PORT5001.envHost port of the Synology sensor’s HTTPS listener (IPv4 only)
SUCKERPOT_ALLOW_DEFAULT_SECRETS0.env1 turns off the secret length and uniqueness checks. CI and throwaway tests only.

Install, updates and sensor switches (M28)

Section titled “Install, updates and sensor switches (M28)”

These variables are written by the installer, or changed by the update and sensor commands. The install guide explains them in context: Install SuckerPot and Updates and rollback.

Each sensor has a switch. 1 turns it on, 0 turns it off. A sensor that is off is not started and publishes no port. Change them with just sensors, or by editing .env and running just up. The default is 1, so an older .env without these lines keeps every sensor.

VariableSensorDefaultStandard port
SENSOR_SSHSSH122
SENSOR_TELNETTelnet123
SENSOR_WEBWeb (HTTP and HTTPS)180, 443
SENSOR_RDPRDP13389
SENSOR_REDISRedis16379
SENSOR_DOCKERDocker API12375
SENSOR_MYSQLMySQL13306
SENSOR_MSSQLMSSQL11433
SENSOR_ADBADB15555
SENSOR_SMBSMB1445
SENSOR_SIPSIP15060 (UDP and TCP)
SENSOR_SYNOLOGYSynology DSM15000, 5001

The port guard (scripts/check-ports.sh) accepts only 0 or 1. Host ports are in Ports to forward.

VariableDefaultSourceDescription
SUCKERPOT_INSTALL_MODEsource.env, written by the installerimages runs the signed release images. source builds them from the checkout. just up and the update scripts read it.
SUCKERPOT_IMAGE_PREFIXunset.env, written in images modeRegistry and name prefix of the release images, ghcr.io/<owner>/suckerpot. Unset, the images are built locally and named local/<project>-<service>. The installer uses the project registry unless you set this variable before you run it. The owner in this name is also the repository whose release signatures are checked.
SUCKERPOT_VERSIONdev.env, written by the installer and by just updateRelease tag of the images to run, such as vX.Y.Z. Every image name ends with it.
SUCKERPOT_VOLUME_PREFIXnone, required.env, written by the installerPrefix of the external data volumes, such as suckerpot_db-data. Compose refuses to start without it.
SUCKERPOT_BUILD_VERSIONdevSet by just up and just updateVersion baked into images built from the checkout. Leave it unset for a plain build.
SUCKERPOT_BUILD_COMMITunknownSet by just up and just updateCommit baked into images built from the checkout.
VariableDefaultSourceDescription
UPDATE_CHECK_ENABLEDfalse.env, or just set UPDATE_CHECK_ENABLED=trueOnce a day, ask GitHub for the latest release, through the VPN only. Never installs anything. See the update check.
UPDATE_CHECK_REPOthe project repository, owner/name.envThe repository the check reads. A fork sets its own. Any value that is not owner/name falls back to the default. just set does not accept this variable.
VariableDefaultSourceDescription
COMPOSE_FILEunset.env, written by the installer for low memorySet to docker-compose.yml:docker-compose.lowmem.yml to use the lower memory limits. See low-memory hosts. just set does not change it.
REDIS_MAXMEMORY_MB192 (96 in low-memory mode).envMemory ceiling of Redis in MiB, from 32 to 4096.
COMPOSE_PROFILESunset (the installer writes egress).envegress runs vpn, collector and analyzer. The installer always sets it. Without a VPN configuration, vpn waits in idle mode. See the VPN before a WireGuard file is uploaded.
VariableDefaultSourceDescription
POSTGRES_DBsuckerpot.envDatabase name
POSTGRES_USERsuckerpot.envOwner account. Used by db and migrate only.
POSTGRES_PASSWORDnone.env, requiredOwner password
POSTGRES_INGEST_USERsuckerpot_ingest.envRole name used by ingest
POSTGRES_INGEST_PASSWORDnone.env, requiredPassword of the ingest role
POSTGRES_API_USERsuckerpot_api.envRole name used by the API
POSTGRES_API_PASSWORDnone.env, requiredPassword of the API role
POSTGRES_HOSTdbFixed by ComposeDatabase host on the backend network
POSTGRES_PORT5432Fixed by ComposeDatabase port

Inside ingest and api, Compose sets POSTGRES_USER and POSTGRES_PASSWORD to that service’s own role, not to the owner.

VariableDefaultSourceDescription
REDIS_PASSWORD and the other REDIS_*_PASSWORD secretsnone.env, requiredSee Secrets
EVENTS_MAXLEN10000.envApproximate maximum length of each sensor’s stream (XADD MAXLEN ~) for SSH, Telnet, Web and RDP
EVENTS_MAXLEN_LOW3000.envThe same cap for the seven M9 sensors (Redis, Docker, MySQL, MSSQL, ADB, SMB and SIP) and the Synology sensor. Kept short so a burst on seven more streams cannot fill Redis memory. Ingest drains the streams, and a sensor drops an event when XADD fails.
DEMO_EVENTS_MAXLEN100000.envThe same cap, for the demo generator only. Keep it above the number of events per sensor in a backfill, or entries ingest has not read yet are trimmed.
EVENTS_MAX_BYTES_PER_SEC200000Code defaultPublisher backstop for every sensor: a token bucket on serialized event bytes per second. Beyond it the publisher drops events and counts them.
EVENTS_MAX_BYTES_BURST2000000Code defaultBurst of that bucket, in bytes
REDIS_HOSTredisFixed by ComposeRedis host
REDIS_PORT6379Fixed by ComposeRedis port
REDIS_USERper serviceFixed by Composeingest, demo, and one sensor-<name> user per sensor
EVENTS_STREAMper serviceFixed by ComposeStream a sensor writes to (events:ssh). The code default is events.

Inside each service, Compose sets REDIS_PASSWORD to the password of that service’s own Redis user.

VariableDefaultSourceDescription
API_ENABLE_DOCSfalse.envServe /docs, /redoc and /openapi.json. Keep false unless you are developing.
API_ALLOWED_ORIGINSempty.envExtra browser origins for the live WebSocket, comma-separated, full origins such as https://dash.example.com. Needed behind a TLS-terminating tunnel. Same-host origins are always allowed.
WEB_DIST_DIR/app/webCode defaultFolder with the built web UI. Served only when it contains an index.html.

The owner account lives in .env: it is always an administrator and works even when the database is down. Colleagues get their own accounts, with the role admin or viewer, from Settings > Users (see Users and roles). See API for how sign-in works.

VariableDefaultDescription
ADMIN_USERNAMEadminName of the owner account. A user created in Settings cannot take this name.
ADMIN_PASSWORD_HASHnone, requiredArgon2 hash of the admin password. Make it with docker compose run --rm api python -m api.hashpw. Put it in .env in single quotes, because it contains $.
SESSION_SECRETnone, requiredSigns the session and CSRF cookies. At least 32 bytes. Generate it with openssl rand -base64 48. Different from every other secret.
SESSION_TTL_HOURS12How long a login lasts
COOKIE_SECUREtrueCookies are Secure and named __Host-sp_session and __Host-sp_csrf. Keep true, also behind a TLS-terminating tunnel (the browser talks HTTPS to the tunnel). false gives the plain names sp_session and sp_csrf without Secure: local development over HTTP only.
LOGIN_RATE5Login attempts per client address per minute (every attempt counts)
LOGIN_RATE_GLOBAL30Wrong passwords per minute for the whole server; successful sign-ins do not count
LOGIN_RATE_USER10Wrong passwords per minute for one username (any letter case), from any address. Not applied to the owner’s name.
AUTH_TRUSTED_PROXIESemptyComma-separated CIDR list of proxy addresses (such as 203.0.113.0/24). Only from those peers does the API read CF-Connecting-IP, or the rightmost non-proxy X-Forwarded-For entry, as the client address. Empty means the headers are never trusted. Replaces the old AUTH_TRUST_PROXY_HEADERS.
API_ALLOWED_HOSTSemptyComma-separated host names the API answers to. If empty, the hosts of API_ALLOWED_ORIGINS are used. localhost and 127.0.0.1 are always allowed. If both are empty, any host is allowed and the API logs a warning: set one in production, for example the tunnel’s host name.
API_AUTH_DISABLEDfalseTurns authentication off. It takes effect only together with SUCKERPOT_DEMO=1, and Compose sets both only through docker-compose.ci.yml (just up-test). The base docker-compose.yml never does. Never use it in production.
API_ALLOWED_ORIGINSemptyAlso used by login: when set, the full origin of a login request (scheme, host, port) must be listed. When empty, only the host is compared with the Host header. Set it behind a tunnel.

The API refuses to start when the hash or the secret is missing or weak. SUCKERPOT_ALLOW_DEFAULT_SECRETS=1 accepts a weak setup for CI only.

Added in M14. There is nothing to configure in .env for it (apart from LOGIN_RATE_USER above): users are created and managed in Settings > Users, by an administrator.

OwnerAdministratorViewer
Where it lives.envdatabasedatabase
Look at every page, export data, watch Liveyesyesyes
Change settings and provider keysyesyesno
Manage users, read the audit logyesyesno
Password changedin .env onlyby the userby the user
Can be disabled, demoted or deletednoyes, by another administratoryes
  • A new account gets a strong temporary password that the administrator sees once. The person must choose their own password at the first sign-in; until then nothing else works.
  • Passwords have 12 to 256 characters, are not one of about a thousand very common passwords (the list is api/api/data/common-passwords.txt) and must not contain the username.
  • Five wrong passwords from one address lock that address for that account for a minute (longer on repeats, up to 15 minutes); 50 in total lock the account for 15 minutes, but only for addresses the user never signed in from. An administrator can unlock at once.
  • Every change to a user asks the administrator for their own password again, and is written to the audit log (kept for one year).
  • Changing a password, disabling an account, changing its role, deleting it or choosing “Sign out everywhere” ends all of that user’s sessions immediately.
  • Optional two-factor sign-in is not built yet.

Losing the owner password: make a new hash with docker compose run --rm api python -m api.hashpw, put it in .env as ADMIN_PASSWORD_HASH and restart the API. The owner account never depends on the database, so this works even when users cannot sign in.

VariableDefaultDescription
SITE_HOME_LAT, SITE_HOME_LON46.8, 8.2Where attack arcs land on the Live map. Rounded to one decimal. Use a country-level point, never your real address.
SITE_HOME_LABELSwitzerlandName shown next to the home point on the Live map (64 characters at most)

Defaults are set in code. Compose passes INGEST_STREAMS through from .env. The others can only be changed with an override file.

VariableDefaultSourceDescription
INGEST_STREAMSevents:ssh,events:telnet,events:web,events:rdp,events:redis,events:docker,events:mysql,events:mssql,events:adb,events:smb,events:sip,events:synology.envStreams to read, comma-separated. Leave it unset: an override must list every sensor, and ingest logs a warning for a built-in stream it leaves out. Letters, digits and _ . : -, up to 64 characters each. The suffix after the last : must be the sensor name: events whose sensor differs are dropped.
INGEST_BATCH_SIZE100Code defaultEvents per database write, 1 to 500
INGEST_BLOCK_MS1000Code defaultHow long one stream read waits for new entries, in milliseconds
INGEST_CLAIM_IDLE_MS60000Code defaultPending entries idle for longer than this are reclaimed
INGEST_RECLAIM_INTERVAL_S60Code defaultHow often ingest looks for stale pending entries
INGEST_GROUPingestCode defaultConsumer group name
INGEST_CONSUMERingest-1Code defaultConsumer name inside the group. Keep it stable: a stable name makes restarts take over their own pending entries.
INGEST_HEARTBEAT_PATH/tmp/ingest-heartbeatCode defaultFile touched on each healthy loop. The health check reads it.

The geolocation data has a licence that requires credit. See Data sources and attribution.

VariableDefaultDescription
GEOIP_PROVIDERautoauto (MaxMind if both MaxMind variables are set, else DB-IP), maxmind, dbip or off
GEOIP_DIR/data/geoipFolder for the downloaded MMDB files
GEOIP_REFRESH_DAYS30Re-download a database when it is older than this (at least 1)
GEOIP_FAKE_FOR_PRIVATEfalseGive private and documentation addresses a fake country. For demo screenshots only.
SETTINGS_ENCRYPTION_KEYemptyEnables entering the keys below on the Settings page. See Enrichment keys in Settings.
MAXMIND_ACCOUNT_IDemptyMaxMind account ID for GeoLite2 downloads
MAXMIND_LICENSE_KEYemptyMaxMind licence key. Optional.
ABUSEIPDB_API_KEYemptyAbuseIPDB key. Without it, no lookups.
ABUSEIPDB_DAILY_QUOTA1000Maximum AbuseIPDB checks per day. Enforced, and persisted in the database.
GREYNOISE_API_KEYemptyGreyNoise Community key. Without it, no lookups.
GREYNOISE_DAILY_QUOTA50Maximum GreyNoise checks per day
CVE_WATCH_ENABLEDtrueDaily job that mirrors CISA KEV, NVD CVEs from the last 60 days and GitHub advisories. The feeds are read in chunks. See Architecture.
NVD_API_KEYemptyNVD key. Optional: raises the rate limit from 5 to 50 requests per 30 s
GITHUB_ADVISORY_TOKENemptyToken for the public GitHub advisories API. Optional: raises the limit from 60 requests per hour. Use a fine-grained token for public repositories only, read-only, with no permissions. Never your push token
CVE_NVD_DAILY_QUOTA200Maximum NVD requests per day. Enforced, and persisted in the database
CVE_GITHUB_DAILY_QUOTA100Maximum GitHub advisory requests per day
REPUTATION_MIN_EVENTS10An address is looked up only after this many events
REPUTATION_INTERVAL_S30Seconds between reputation cycles (at least 5)
ABUSECH_AUTH_KEYemptyabuse.ch Auth-Key (free) for URLhaus and ThreatFox. Without it, no indicator lookups there.
ABUSECH_DAILY_QUOTA1000Maximum abuse.ch requests per day (URLhaus and ThreatFox share it). Enforced, and persisted.
VIRUSTOTAL_API_KEYemptyVirusTotal key. Used for file hashes only.
VIRUSTOTAL_DAILY_QUOTA400Maximum VirusTotal requests per day (the free tier allows 500). Requests are also spaced 16 s apart.
INTEL_INTERVAL_S30Seconds between threat-intel cycles (at least 5)
INDICATORS_ENABLEDtrueExtract indicators from recorded events
INDICATORS_IGNOREemptyComma-separated host names, addresses and CIDR blocks never recorded as indicators: set it to your public IP and domains before going live. A name also covers its subdomains. The hosts in API_ALLOWED_HOSTS and API_ALLOWED_ORIGINS are added automatically.
INDICATORS_KEEP_DOCUMENTATIONfalseKeep documentation addresses and example domains as indicators. Demo events do not need it (they carry demo: true). They are never sent to an intel service either way.
INDICATORS_MAX_ROWS500000Most indicator rows kept (1,000 to 50 million). Read by migrate. The hourly job drops the least sighted, oldest single-source ones first.
INDICATORS_LINK_CAP500Most sessions linked to one indicator. Its counters stay exact beyond that.
RULES_DIRingest/rules (/app/rules in the image)Folder with the tagging rules (*.yaml)

You can enter, replace and remove the AbuseIPDB, GreyNoise, MaxMind, abuse.ch and VirusTotal keys on the Settings page, with no restart.

  1. Generate an encryption key and add it to .env: SETTINGS_ENCRYPTION_KEY=$(openssl rand -base64 32). Then run docker compose up -d so api and ingest see it. Without it, the Settings page shows the keys as read-only and says why.
  2. On the Settings page, choose Set key (or Replace) for a provider, paste the key and type the admin password to confirm. The key is encrypted before it is stored and is never shown again, only its last four characters.
  3. Ingest checks for changes every 30 seconds, so a new key is used within a minute. A new MaxMind key also starts a database download.
  4. Test key makes one real call to the provider and counts it against the daily quota. The result appears on the page.

A key set in Settings overrides the same key in .env. Remove it and the .env key is used again. The page shows where each key comes from. If you change or lose SETTINGS_ENCRYPTION_KEY, the saved keys cannot be decrypted: the page says so, and the .env keys keep working until you enter the keys again. See Security for the threat model.

The variables in the first group are passed through from .env. The second group is read by the sensor but not passed through by Compose. See SSH sensor.

VariableDefaultDescription
SSH_HOSTNAMEsrv-web01Host name shown to clients
SSH_BANNERSSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14SSH version string. Keep it consistent with SSH_OS_VERSION.
SSH_ACCEPT_CREDENTIALSroot:root,root:123456,root:password,admin:admin,ubuntu:ubuntu,pi:raspberry,user:userComma-separated user:password pairs that log in. Weak on purpose.
SSH_ACCEPT_RANDOM_PROB0.0Probability (0 to 1) of accepting any non-empty password
SSH_MAX_AUTH_ATTEMPTS6Failed attempts per connection
SSH_MAX_CONNECTIONS200Simultaneous connections, in total
SSH_MAX_PER_IP5Simultaneous connections per source address
SSH_PORT2222Port inside the container (fixed by Compose)
SSH_PUBLIC_PORT22Port recorded as dst_port (set from SSH_HOST_PORT)
SSH_HOSTKEY_DIR/data/host_keysHost key folder, on a volume (fixed by Compose)
SSH_LOGIN_TIMEOUT, SSH_IDLE_TIMEOUT, SSH_MAX_SESSION30, 300, 1800Seconds: to log in, idle, whole session
SSH_MAX_LINE4096Longest accepted input line
SSH_EVENTS_PER_SEC20connect events per source address per second
SSH_GLOBAL_EVENTS_PER_SEC200Sensor-wide event cap
SSH_CONN_RATE_PER_IP, SSH_CONN_BURST_PER_IP1, 10New connections per source address per second, and the burst
SSH_SUMMARY_INTERVAL60Seconds between summaries of suppressed events (at least 5)
SSH_LINE_BUDGET0.15CPU seconds allowed per command line
SSH_MEMORY_BUDGET_MB48Memory budget over all sessions
SSH_MEMORY_SHARE_PER_IP0.25Share of the memory budget one source address may hold (0.01 to 1)
SSH_MEMORY_LIMIT_MB256Memory limit the guard compares against when the cgroup limit cannot be read. Keep it equal to the container limit.
SSH_MEMORY_PRESSURE0.70Fraction of the limit above which new connections are refused and the biggest connections stop being read
SSH_MEMORY_CRITICAL0.85Fraction above which the biggest connections are closed
SSH_CHANNEL_WINDOW_KB32Flow-control window of a session channel, in KiB (8 to 1024). A session with more than four windows of unread input is closed.
SSH_INBOUND_CONN_KB_PER_SEC32Inbound data one connection may send per second, in KiB (metered before decryption). Also caps scp uploads.
SSH_INBOUND_KB_PER_SEC256The same, for all connections together
SSH_SHELL_CPU_SHARE0.3CPU-seconds of shell time per second for all sessions together (0.05 to 0.9), shared fairly between source addresses
SSH_SHELL_CPU_BURST0.2Burst of that budget, in CPU-seconds
SSH_IP_EVENT_BYTES_PER_SEC, SSH_IP_EVENT_BYTES_BURST20000, 1000000Event bytes per source address per second, and the burst
SSH_GLOBAL_EVENT_BYTES_PER_SEC, SSH_GLOBAL_EVENT_BYTES_BURST200000, 2000000Event bytes per second for the whole sensor, and the burst. Over budget, an event first loses its bulk (output_omitted, replay_omitted), then is dropped.
SSH_STATS_INTERVAL60Seconds between the sensor’s own statistics log lines (at least 5)
SSH_MAX_COMMANDS5000Commands per session
SSH_FAKE_IPderivedPrivate address shown by ifconfig and ip a
SSH_OS_VERSION, SSH_KERNEL, SSH_KERNEL_BUILD24.04.4, 6.8.0-100-generic, matching buildFake OS and kernel versions
VariableDefaultDescription
WEB_HOSTNAMEsrv-web01Host name (a plain label)
WEB_HTTP_PORT, WEB_HTTPS_PORT8080, 8443Ports inside the container
WEB_HTTP_PUBLIC_PORT, WEB_HTTPS_PUBLIC_PORT80, 443Ports recorded as dst_port and shown in error pages
WEB_DATA_DIR/dataFolder for the generated profile and TLS certificate (a volume)
WEB_SERVER_TOKENApache/2.4.58 (Ubuntu)Server header. Must look like Apache/2.4.N (Ubuntu).
WEB_PHP_VERSION8.3.6PHP version shown. Must be 8.3.N with N below 36, the PHP2Shell-vulnerable range.
WEB_EXPOSE_PHPtrueSend X-Powered-By on PHP responses
WEB_WP_VERSION, WEB_PMA_VERSION6.8.1, 5.2.1WordPress and phpMyAdmin versions shown
WEB_UPLOAD_PROGRESS_NAMEPHP_SESSION_UPLOAD_PROGRESSName of the upload-progress field the detector watches
WEB_MAX_CONNECTIONS, WEB_MAX_PER_IP200, 10Connection caps
WEB_MEMORY_BUDGET_MB48Memory budget over all requests
WEB_EVENTS_PER_SEC, WEB_GLOBAL_EVENTS_PER_SEC20, 200Event caps per source address and for the sensor

Set these in .env. Compose passes them to the sensor under the SENSOR_RDP_ names shown in the first column. The .env names are in the second column, and .env.example lists them with comments.

VariableDefaultDescription
SENSOR_RDP_NLA_MODE (.env: RDP_NLA_MODE)requiredrequired accepts NLA clients only. optional also accepts TLS-only clients and records the cleartext password they send. This is an owner opt-in, see Security. Any other value stops the sensor at start-up.
SENSOR_RDP_ACCOUNTS (.env: RDP_ACCOUNTS)administrator:P@ssw0rd,administrator:123456,admin:adminDecoy user:password pairs, comma separated. Only these pairs are checked, and the pairs are public by design.
RDP_PORT3389Port inside the container
RDP_PUBLIC_PORT3389Port recorded as dst_port
RDP_HOSTNAMEderivedNetBIOS name in the certificate, 1 to 15 characters
RDP_DATA_DIR/dataProfile secret, certificate and key (a volume)
RDP_ENABLE_TLStrueComplete the TLS handshake after selecting CredSSP
RDP_MAX_CONNECTIONS, RDP_MAX_PER_IP200, 8Connection caps
RDP_MAX_TLS_PER_IP4TLS handshakes allowed from one source at the same time (.env uses the same name)
RDP_FIRST_DATA_TIMEOUT8Seconds to wait for the first bytes
RDP_HANDSHAKE_TIMEOUT10Seconds per PDU and for the TLS handshake
RDP_CREDSSP_TIMEOUT10Seconds to wait for the first TSRequest
RDP_MAX_SESSION60Hard cap on one connection, in seconds
RDP_SESSIONS_PER_SEC_PER_IP2Recorded connections per source per second (burst 15 times that)
RDP_GLOBAL_SESSIONS_PER_SEC50Recorded connections per second overall (burst 2 times that)
RDP_GLOBAL_EVENTS_PER_SEC200Global event cap (all types except disconnect)
RDP_DISCONNECT_EVENTS_PER_SEC60Global cap on disconnect events
RDP_TLS_PER_SEC_PER_IP, RDP_TLS_BURST_PER_IP5, 20TLS handshakes started per source
RDP_MAX_TLS_HANDSHAKES32TLS handshakes in progress at the same time
VariableDefaultDescription
TELNET_PORT2323Port inside the container
TELNET_PUBLIC_PORT23Port recorded as dst_port
TELNET_PERSONALITYdvrdvr or router
TELNET_HOSTNAMEderivedHost name (a plain label)
TELNET_LOGIN_PROMPT{hostname} login: Login prompt. \r and \n are turned into line breaks.
TELNET_PRELOGIN_BANNER, TELNET_MOTDemptyTexts before the login and after it. Empty means the personality’s own text. At most 2048 characters.
TELNET_DATA_DIR/dataFolder for the generated profile secret (a volume)
TELNET_ACCEPT_CREDENTIALStrimmed Mirai listComma-separated user:password pairs that log in
TELNET_ACCEPT_RANDOM_PROB0.0Probability (0 to 1) of accepting any login
TELNET_MAX_AUTH_ATTEMPTS6Failed attempts per connection
TELNET_MAX_CONNECTIONS, TELNET_MAX_PER_IP200, 8Connection caps
TELNET_LOGIN_TIMEOUT, TELNET_IDLE_TIMEOUT, TELNET_MAX_SESSION60, 300, 1800Seconds
TELNET_MAX_LINE, TELNET_MAX_COMMANDS4096, 2000Longest input line, commands per session
TELNET_LINE_BUDGET0.15CPU seconds per command line
TELNET_MEMORY_BUDGET_MB48Memory budget over all sessions
TELNET_EVENTS_PER_SEC, TELNET_GLOBAL_EVENTS_PER_SEC20, 200Event caps per source address and for the sensor
TELNET_CONN_RATE_PER_IP, TELNET_CONN_BURST_PER_IP1, 10New connections per second per source address, and the burst
TELNET_SUMMARY_INTERVAL60Seconds between summaries of unrecorded connections (at least 5)

Seven sensors were added in M9: Redis, Docker, MySQL, MSSQL, ADB, SMB and SIP. Compose wires each one with its own Redis user, stream and network. It sets the public port, the connection caps and the memory limit. The table lists the variables you can set in .env. The sensor’s own tunables (timeouts, budgets, rate limits) keep their defaults unless you add them with an override file. Each sensor page lists them, and the sensor README has the full table.

VariableDefaultSensorDescription
REDIS_MAX_CONNECTIONS, REDIS_MAX_PER_IP200, 8RedisConnection caps, in total and per source address
DOCKER_MAX_CONNECTIONS, DOCKER_MAX_PER_IP200, 8DockerConnection caps
MYSQL_MAX_CONNECTIONS, MYSQL_MAX_PER_IP200, 8MySQLConnection caps
MYSQL_ACCOUNTSroot:,root:root,admin:admin,mysql:mysqlMySQLComma-separated user:password pairs that log in. Weak on purpose.
MSSQL_MAX_CONNECTIONS, MSSQL_MAX_PER_IP200, 8MSSQLConnection caps
ADB_MAX_CONNECTIONS, ADB_MAX_PER_IP200, 8ADBConnection caps
SMB_MAX_CONNECTIONS, SMB_MAX_PER_IP200, 10SMBConnection caps
SMB_HOSTNAME, SMB_DOMAINempty (derived)SMBNetBIOS computer name (1 to 15 characters) and domain or workgroup. Empty means a realistic name derived from the sensor’s secret.
SMB_SMB1_MODEsteerSMBsteer switches SMB1 clients to SMB2 and records them. reset records and drops them.
SIP_MAX_CONNECTIONS, SIP_MAX_PER_IP200, 8SIPTCP connection caps
SIP_OWN_IPSemptySIPComma-separated addresses of this host. The sensor never answers them, so it cannot be made to talk to itself.

Each of these sensors needs its own Redis password in .env (REDIS_SENSOR_<NAME>_PASSWORD). See Secrets. Each host port must differ from the others and from DASHBOARD_PORT. The port guard checks this.

The Synology sensor (M20) has its own Redis user, stream and network, like the others. Compose sets its public ports, its connection caps and its memory limit. The ports are in Compose and host. The table lists the variables you can set in .env. The sensor README has the full list of tunables.

VariableDefaultDescription
SYNOLOGY_MAX_CONNECTIONS, SYNOLOGY_MAX_PER_IP200, 8Connection caps, in total and per source address
SYNOLOGY_CONN_RATE, SYNOLOGY_CONN_BURST5, 20New connections per second per source address, and the burst
SYNOLOGY_MEMORY_BUDGET_MB80Memory for connections and request bodies in flight. Compose sets SYNOLOGY_MEMORY_LIMIT_MB to 160, the container limit.
SYNOLOGY_EVENTS_PER_SEC, SYNOLOGY_GLOBAL_EVENTS_PER_SEC20, 200Event caps per source address and for the sensor
SYNOLOGY_HTTP_REDIRECTfalseRedirect the HTTP login page to HTTPS, as a DSM with forced HTTPS does. Off by default: DSM serves the UI on port 5000.

The Redis password is REDIS_SENSOR_SYNOLOGY_PASSWORD. See Secrets. The sensor page describes what the sensor emulates and records.

VariableDefaultSourceDescription
DEMO_DAYS30.envNumber of days the demo data covers
DEMO_SESSIONS3000.envNumber of sessions the backfill creates
DEMO_SEED42.envRandom seed. The same seed gives the same data.

See Demo data for the command-line options that override these.

VariableDefaultDescription
RETENTION_DAYS90Days to keep raw events (7 to 3650). Aggregates are kept forever. Read by the migrate service, which re-applies the policies on every run. See Data retention.
DB_MAX_SIZE_GB10Size ceiling of the database (at least 1). Above 90% the disk guard drops the oldest raw chunks down to 80%. Above 95% ingest keeps only payload previews. Read by migrate and ingest.

Details in Defend. Ingest scores indicators into blocklists when BLOCKLIST_ENABLED=true; the API serves a token-protected feed when FEEDS_ENABLED=true. Both are off by default.

VariableDefaultDescription
BLOCKLIST_ENABLEDfalseScore addresses, hosts, URLs and hashes into the blocklists (ingest, every 15 minutes)
BLOCKLIST_WINDOW_DAYS30How many days of sessions and indicators to score (1 to 365)
BLOCKLIST_MAX_ROWS20000Most entries kept, best score first (100 to 1,000,000)
CONTACTS_SHARED_ASNSGoogle, Cloudflare, Microsoft, Akamai, Fastly, Meta, AppleComma-separated AS numbers whose addresses are dropped from VirusTotal-sourced probable-C2 contacts (read from the local ASN database, no lookup). Replaces the default list. ThreatFox C2 entries are never filtered by network. Pass it to the ingest service in docker-compose.yml to use it.
BLOCKLIST_NEVERemptyAddresses, networks and names never listed (also read: AUTH_TRUSTED_PROXIES, API_ALLOWED_HOSTS, INDICATORS_IGNORE)
FEEDS_ENABLEDfalseServe /feeds/* with bearer tokens made in Settings. The only part of the API outside the dashboard login

Details in Architecture. Both switches are off by default. Compose passes these variables to ingest and api.

VariableDefaultDescription
ATTACK_ENABLEDfalseSync the MITRE ATT&CK catalogue every hour and label sessions with techniques
CAMPAIGNS_ENABLEDfalseCluster finished sessions into campaigns, every 5 minutes
CAMPAIGN_MIN_SOURCES2Distinct sources a shared key needs before it makes a campaign (2 to 100)
CAMPAIGN_LINK_CAP500Most sessions linked to one campaign (10 to 10,000)
CAMPAIGNS_MAX_ROWS50000Row ceiling of the campaign tables. The oldest, least seen rows go first (1,000 to 50 million)

Details in Architecture. The explainer is off by default. When it is on, it sends the normalised command text of one script (addresses, host names and URLs are replaced by placeholders) to the Anthropic API, and nothing else. Set the key with ANTHROPIC_API_KEY. The API also accepts it as provider anthropic (PUT /api/v1/settings/providers/anthropic), which overrides the variable, but the Settings page does not show a card for it yet.

VariableDefaultDescription
SCRIPTS_ENABLEDfalseFingerprint sessions with two or more commands into the script library, every 5 minutes
SCRIPTS_MAX_ROWS100000Row ceiling of the script tables (1,000 to 50 million)
LLM_ENABLEDfalseAllow the optional LLM explanation. An administrator still has to ask for each one (“Explain now”).
LLM_DAILY_QUOTA50Calls to the model per UTC day, 0 to 10,000. Kept in the database, so a restart does not reset it.
LLM_MONTHLY_TOKEN_CAP2000000Tokens per UTC month, 0 to 1 billion. Kept in the database.
LLM_MODELclaude-haiku-5-5Model name: lower-case letters, digits, -, . and _, at most 64 characters
ANTHROPIC_API_KEYemptyAPI key for the explainer. Needed only when LLM_ENABLED=true.

Details in Architecture. Alerts and the report are off by default. Discord and Teams targets are not set here. You add them in the dashboard (Alerts), where they are checked and stored encrypted. Every rule starts off, and you switch on each one yourself.

VariableDefaultDescription
ALERTS_ENABLEDfalseEvaluate the alert rules and deliver messages
REPORTS_ENABLEDfalseBuild the weekly report after each Monday 06:00 (site time)
ALERTS_DASHBOARD_URLemptyPublic https address of the dashboard, with no query string. Alert messages link to the matching page. Without it, messages have no links.
SITE_TIMEZONEUTCIANA time zone (for example Europe/Zurich) that decides when the week starts for the report
SMTP_HOSTemptySMTP server for e-mail alerts. E-mail needs SMTP_HOST and SMTP_FROM.
SMTP_PORT587, or 465 with SMTP_SECURITY=sslSMTP port (1 to 65535)
SMTP_SECURITYstarttlsstarttls (usually port 587) or ssl (usually port 465). Plain text is not offered. The certificate is always checked.
SMTP_USERemptyLogin name. Leave empty for a server that needs no login.
SMTP_FROMemptySender address of the alerts (a plain e-mail address)
SMTP_PASSWORDemptyLogin password. It can also be set in Settings, under provider smtp.

Discord and Teams webhooks need no variable. They are stored as channels in the database. The outbound hosts are listed in Architecture.

Today, global search, the glossary and the research-scanner switch need no variable. Research scanners are labelled by the network that owns them, and they are left out of lists unless a page asks for them (include_benign). The list of scanners is in ingest/rules/scanners.yaml.

Anonymous egress and payload collection (M21)

Section titled “Anonymous egress and payload collection (M21)”

Details in Architecture and Deployment. Every outbound request of the stack goes through a WireGuard tunnel in the vpn service. There is no direct mode: without a tunnel, the outbound features stay idle.

VariableDefaultDescription
COMPOSE_PROFILESunsetAdd egress to run vpn, collector and analyzer. Without it the stack is healthy but has no outbound traffic.
VPN_WG_PRIVATE_KEYemptyInterface private key from your provider’s WireGuard config. A secret.
VPN_WG_PEER_PUBLIC_KEYemptyPublic key of the provider’s server
VPN_WG_ADDRESSESemptyInterface addresses, comma-separated. IPv6 entries are ignored.
VPN_WG_DNSemptyThe provider’s DNS address (informational: names resolve through the tunnel)
VPN_WG_ENDPOINT_IPemptyServer address, without the port (a public IPv4 address)
VPN_WG_ENDPOINT_PORTemptyServer UDP port
EGRESS_PROXY_PASSWORDemptyPassword of the vpn HTTP proxy. At least 24 characters, required with the profile. A secret.
EGRESS_PROXY_URLhttp://vpn:8888Proxy used by ingest and collector
EGRESS_CONTROL_URLhttp://vpn:8000Control server (read-only status routes)
EGRESS_ECHO_URLShttps://api.ipify.org,https://icanhazip.comServices that answer with the caller’s public address. At least two must agree.
EGRESS_ALLOWED_CIDRSemptyNetworks the VPN exit address must belong to, comma-separated. Empty means the /24 of VPN_WG_ENDPOINT_IP. Set it when your provider exits elsewhere.
EGRESS_CHECK_INTERVAL_S300Seconds between egress self-checks
POSTGRES_COLLECTOR_PASSWORD, POSTGRES_ANALYZER_PASSWORDemptyPasswords of the collector and analyzer database roles. At least 24 characters, all different from each other and from the other secrets. Required with the profile. Secrets.
POSTGRES_COLLECTOR_USER, POSTGRES_ANALYZER_USERsuckerpot_collector, suckerpot_analyzerRole names
COLLECTOR_ENABLEDfalseFetch captured download URLs and analyse the files. Off by default.
COLLECTOR_POLL_S15Seconds between two looks for new URLs
COLLECTOR_MAX_BYTES16777216Largest file kept (16 MiB)
COLLECTOR_TIMEOUT_S60Total time for one fetch
COLLECTOR_CONNECT_TIMEOUT_S10Connect time for one fetch
COLLECTOR_MAX_REDIRECTS3Redirects followed, each one validated
COLLECTOR_CONCURRENCY2Fetches in flight (one per host)
COLLECTOR_HOST_INTERVAL_S10Seconds between two requests to the same host
COLLECTOR_HOURLY_QUOTA, COLLECTOR_DAILY_QUOTA60, 500Fetches per hour and per day
COLLECTOR_REFETCH_HOURS24Minimum age before a URL that was seen again is fetched again
COLLECTOR_MAX_DEPTH2How many levels of next-stage URLs found inside fetched scripts are followed
COLLECTOR_DENY_CIDRSemptyExtra networks the collector must never contact (your own ranges), comma-separated
QUARANTINE_MAX_MB2048Size cap of the quarantine volume. At the cap, fetching stops.
QUARANTINE_RETENTION_DAYS90Days to keep a file after it was last seen. Its row and analysis stay.
ANALYZER_TIMEOUT_S60Wall-clock limit for analysing one file
ANALYZER_MEMORY_MB384Memory cap of the parser child process

scripts/check-vpn-env.sh (run by just up and just config) checks these settings and prints variable names only, never values. It refuses a missing endpoint, a private endpoint address, a proxy password under 24 characters, and missing or repeated database passwords.

FETCH_PAYLOADS, PAYLOAD_MAX_BYTES and PAYLOAD_TIMEOUT_SECONDS no longer exist. COLLECTOR_ENABLED, COLLECTOR_MAX_BYTES and COLLECTOR_TIMEOUT_S replace them.

Details in Architecture, Deployment and Security. The worker is off by default. It only prepares drafts; it sends nothing unless you set up SMTP sending in Settings > Reporting (off by default, M25 design). RDAP lookups go through the VPN egress gate, so the egress profile must be on.

VariableDefaultDescription
ABUSE_REPORTS_ENABLEDfalseRun the abuse worker in ingest
ABUSE_BUILD_INTERVAL_S3600Seconds between two draft builds (60 to 86400)
ABUSE_MIN_LOGIN_ATTEMPTS50Failed logins that select a source. A login followed by commands, a download attempt or a severe tag also selects it.
ABUSE_SEVERE_TAGSbuilt-in listTags that select a source: malware-drop, mirai, crypto-miner, php2shell, exploit and any cve-* tag. Empty means the built-in list.
ABUSE_LOOKBACK_DAYS7Days of evidence in a report
ABUSE_SETTLE_MIN30Minutes a source must be quiet before it is drafted
ABUSE_RESEND_DAYS14Days before the same source can be reported again to the same recipient
ABUSE_MAX_SUBJECTS100Subjects per report (maximum 100)
ABUSE_MAX_OPEN_DRAFTS200Drafts open at once
ABUSE_RDAP_DAILY_QUOTA, ABUSE_RDAP_HOURLY_QUOTA1000, 200Hard RDAP request quotas. Each redirect and fallback counts.
ABUSE_REPORTS_INCLUDE_DEMOfalseAlso draft from demo data (documentation addresses). Demo drafts can never be sent.
ABUSE_ROUND_TIMEStrueRound times in reports to the minute. Set false to allow exact times.
ABUSE_REPORT_RETENTION_DAYS730Days to keep reports marked sent or closed (the duplicate check needs the history). Discarded drafts are removed after 90 days.

The reply contact, signature and all wording are not environment variables. The owner types them in Settings > Reporting. Only the reply contact is required. To keep the identity guard accurate, the api service also reads the sensor host name variables, API_ALLOWED_ORIGINS, ALERTS_DASHBOARD_URL, INDICATORS_IGNORE, BLOCKLIST_NEVER, AUTH_TRUSTED_PROXIES and VPN_WG_ENDPOINT_IP. These are used only to build the list of terms a report must never contain.

Details in Replies to reports. The worker is off by default. Mail is read only through the VPN, and only when ABUSE_REPLIES_ENABLED is true and you save and enable a mailbox in Settings > Reporting > Replies. The mailbox (server, user name, password, folder, trusted provider name) is never an environment variable. Pasted replies work without any of this.

VariableDefaultDescription
ABUSE_REPLIES_ENABLEDfalseRun the replies worker in ingest. It also processes pasted replies.
ABUSE_REPLY_MAX_BYTES262144Largest mail read, in bytes (65,536 to 1,048,576). Larger mail is cut and marked as truncated.
ABUSE_REPLY_MAX_PER_POLL50Mails read in one poll (1 to 500). The rest waits for the next poll.
ABUSE_REPLY_DAILY_CAP500Mails read per UTC day (1 to 10,000).
ABUSE_REPLY_POLL_DEADLINE_S120Seconds one poll may take in total (30 to 600).
ABUSE_REPLY_EXCERPT_CHARS2000Characters of a reply kept as an excerpt (0 to 2,000). 0 keeps none.
ABUSE_REPLY_EXCERPT_DAYS180Days an excerpt is kept (1 to 3,650). Read by migrate for the retention job.
ABUSE_REPLY_MATCH_DAYS120A reply is matched to reports sent in the last N days (1 to 730).
ABUSE_NO_ANSWER_DAYS14Days without an answer before a sent report shows No answer (1 to 365). Read by api.

The check interval of the mailbox is set in Settings (5 to 1440 minutes, 15 by default), not here.

Takedown tracking and abuse.ch submission (M27)

Section titled “Takedown tracking and abuse.ch submission (M27)”

Details in Takedowns. Everything is off by default. The collector re-checks a payload URL only with TAKEDOWN_ENABLED=true and COLLECTOR_ENABLED=true, through the VPN. Re-checks are one bounded GET of a URL that already served a sample. C2 servers and attacking addresses are never contacted.

abuse.ch submission needs three things: ABUSECH_SUBMIT_ENABLED=true, a mode other than Off in Settings > Reporting > abuse.ch, and an Auth-Key saved there. The key is never an environment variable.

VariableDefaultDescription
TAKEDOWN_ENABLEDfalseRun the takedown worker in ingest and the HTTP re-checks in collector (the latter also needs COLLECTOR_ENABLED=true).
TAKEDOWN_TRACK_DAYS30Days a target is followed after it was reported (7 to 90).
TAKEDOWN_HTTP_DAILY_QUOTA200Re-checks per day, read by collector (at least 1).
TAKEDOWN_INTEL_DAILY_QUOTA100Lookups per day (URLhaus and ThreatFox) that takedown tracking may make. Each one also counts against ABUSECH_DAILY_QUOTA.
TAKEDOWN_ABUSEIPDB_DAILY_QUOTA50AbuseIPDB reads per day that takedown tracking may make. Each one also counts against ABUSEIPDB_DAILY_QUOTA. SuckerPot never reports to AbuseIPDB.
TAKEDOWN_QUIET_DAYS7Days without a sighting before an attacking address counts as quiet (1 to 90).
TAKEDOWN_CHECK_RETENTION_DAYS400Days to keep individual checks (30 to 3,650). Read by migrate. The monthly roll-up is kept forever.
TAKEDOWN_INTERVAL_S600Seconds between two takedown cycles in ingest (30 to 86,400). Read by the worker.
ABUSECH_SUBMIT_ENABLEDfalseServer switch for submitting captured malware URLs to URLhaus, and C2 addresses to ThreatFox. Read by ingest and api. When it is false, the Settings card shows the switch as disabled.

The abuse.ch lookup key (used to check whether a value is already listed) is the same as before: ABUSECH_AUTH_KEY, or the key on the Settings page. The submission Auth-Key is separate and is entered only in Settings > Reporting > abuse.ch.

The network is unlocked by default, and nobody is joined by default. Details are in The SuckerPot network and Network protocol. A system sends nothing until it has joined, through the installer question (the default answer is yes) or in Settings, Network. NETWORK_ENABLED=false switches the whole feature off.

VariableDefaultServiceDescription
NETWORK_ENABLEDtrueingest, apiMaster switch. It only unlocks joining. With false the worker never contacts the hub, and the Network pages explain how to allow it
NETWORK_HUB_URLemptyingest, apiAddress of the hub, for example https://hub.example.org. Must be HTTPS, with no path and no credentials. Set it together with NETWORK_HUB_KEY
NETWORK_HUB_KEYemptyingest, apiThe hub’s public key (base64url, 32 bytes). The node refuses any answer that this key did not sign
NETWORK_INTERVAL_S900ingestSeconds between worker cycles, from 30 to 86400. While a join or a leave is pending, the worker checks every 60 seconds
NETWORK_SUBMIT_DELAY_MIN_H2ingestShortest random wait, in hours, after a UTC day ends before its batch is sent. From 0 to 23
NETWORK_SUBMIT_DELAY_MAX_H20ingestLongest random wait, in hours. From 1 to 23, and above the minimum
NETWORK_MAX_SOURCES5000ingestAttacker addresses per batch. The most severe are kept first. From 1 to 5000

Notes:

  • A wrong value (for example a hub URL with a path, or only one of the URL and the key) keeps the network off, and ingest logs an error. Check its log after you change these.
  • Storing the node key needs SETTINGS_ENCRYPTION_KEY. Without it, joining stops with no_settings_key.
  • NETWORK_ENABLED, NETWORK_HUB_URL and NETWORK_HUB_KEY can be changed with just set (for example just set NETWORK_ENABLED=false). NETWORK_HUB_KEY is the hub’s public key, so it is the one *_KEY name that just set accepts.
  • Joining, the pseudonym, the country and leaving are done in the dashboard, not in .env. The hub’s own settings are in hub/README.md.

Participant mode, verification and automatic updates (M32)

Section titled “Participant mode, verification and automatic updates (M32)”

Participant mode is described in Join the network. The installer writes these lines. They are commented out in .env.example on purpose, so a full install does not carry them.

VariableDefaultServiceDescription
SUCKERPOT_STACK_MODEfullscriptsfull or participant. Chosen at install. ./scripts/install.sh --full switches a participant install to full
COMPOSE_FILEdocker-compose.ymlcomposeIn participant mode: docker-compose.yml:docker-compose.participant.yml
COMPOSE_PROFILESegress (full)composedirect starts hubproxy (no VPN). egress starts the VPN. Never both
EGRESS_MODEvpningestdirect reaches only the hub, through hubproxy. vpn uses the VPN proxy
HUBPROXY_ALLOWthe hub host, port 443hubproxyComma-separated host:443 pairs, at most two. Direct mode only
NETWORK_CONSENTemptyingestConsent record v2:<list>:<UTC day>, written by the installer (participant mode, or a yes to the network question in full mode) or participant.sh join. ingest joins from it once, and never again after a leave. ingest ignores a record older than 7 days. just set NETWORK_CONSENT= clears it; just set refuses any non-empty value. ingest does not join on a version it does not know (consent_outdated)
NETWORK_PSEUDONYMrandomingestOptional pseudonym
NETWORK_COUNTRY, NETWORK_SHOW_COUNTRYempty, falseingestOptional two-letter country, shown only if you opt in and enough members share it
NETWORK_VERIFY_REVEALSfalse (true in participant mode)ingestAnswer the hub when it asks for a link that several sensors saw
NETWORK_VERIFIERfalseingestAsk to be a verifier. Needs a VPN and COLLECTOR_VERIFY_ENABLED. The hub operator must grant the role (verifier_needs_vpn and verifier_needs_collector explain a refusal)
COLLECTOR_VERIFY_ENABLEDfalsecollectorLet the collector fetch links for the network
NETWORK_VERIFY_DAILY_CAP100ingestLocal cap on verification fetches per day
NETWORK_VERIFY_HOST_DAILY_CAP3ingestLocal cap per site per day
SELF_TERMS_AUTO_CONFIRMfalse (true in participant mode)ingestLearned own addresses act without a click
SELF_ADDRESSemptyingestOptional own public address. No script looks it up
AUTO_UPDATE_ENABLEDfalse (true in participant mode)scriptsInstall new signed releases once a day from the user’s crontab. See Updates

The hub address and key are not in .env by default. They ship inside each release (config/network-defaults.env), and an explicit .env value always wins. NETWORK_HUB_KEY accepts up to three comma-separated keys, for a key rollover.

The community blocklist feeds are free and public. A node can fetch them, and firewalls can fetch the public files directly, with or without a node. Details are in Community blocklist feeds. Fetching is off by default, and it does not depend on network membership.

VariableDefaultServiceDescription
NETWORK_FEED_ENABLEDfalseingest, apiFetch the public lists through the VPN gate. Nothing is requested while this is false. The Network tab shows the lists as off until it is true.
NETWORK_FEED_URLemptyingest, apiBase address of the feed, for example https://feeds.example.org/v1. HTTPS only, no query string, no credentials. The API shows it as the copy address. Set it by hand in .env.
NETWORK_FEED_KEYemptyingestThe feed’s minisign public key, starting with RW. This is not the hub key. Set it by hand in .env.
NETWORK_FEED_INTERVAL_S3600ingestSeconds between fetches, from 900 to 86400
NETWORK_FEED_LISTSinbound,outbound,outbound-domainsingestLists to store. Choose from inbound, inbound-high, outbound, outbound-high and outbound-domains. The high lists are subsets, and each stored row carries its tier.
FEEDS_ENABLEDfalseapiServes the token-protected mirror under /feeds. Needed for the local mirror of the community lists (token kind network). The same switch as in Blocklists and feeds (M16).

Notes:

  • NETWORK_FEED_URL and NETWORK_FEED_KEY must be set together. A wrong value leaves the feed unconfigured, and ingest logs an error. Check its log after you change them.
  • just set NETWORK_FEED_ENABLED=true works. NETWORK_FEED_URL and NETWORK_FEED_KEY are not in the just set list, so set them in .env.
  • Turning the fetch off deletes the stored list rows on the next worker cycle. Leaving the SuckerPot network does not delete them.
  • The node checks the signature, the serial (no rollback), valid_until, and the sha256 of every file before it stores anything. If a check fails, the last good copy stays. After valid_until without a new index, the entries are kept for display only, and the local mirror serves nothing.

These names are in the template. The code does not read them, so setting them has no effect today.

VariableDefaultMilestoneDescription

SuckerPot shows data from third parties. Keep these credits visible if you publish a screenshot or a page that shows the data.

DataUsed whenCredit
DB-IP Lite (country, city, ASN)GEOIP_PROVIDER is auto without MaxMind keys, or dbipIP geolocation data by DB-IP.com, CC BY 4.0, https://db-ip.com
MaxMind GeoLite2MAXMIND_ACCOUNT_ID and MAXMIND_LICENSE_KEY are setThis product includes GeoLite2 data created by MaxMind (https://www.maxmind.com)
AbuseIPDB, GreyNoise CommunityTheir API keys are setScores are read only. Respect each service’s terms and daily quota.

The API returns the credit text in GET /api/v1/settings/enrichment (geoip.attribution), and the dashboard shows it. The .mmdb files are downloaded by ingest at run time and are never committed to the repository, because their licences do not allow redistribution.