selfhost.directory

The index of self-hostable projects — ranked, categorized and served with live updates.
Own your data and stop giving it to big tech.

Polis logo

Self-host Polis

v2.42.2

AI-backed group polling for large-scale opinion mapping

ReleaseOpen SourceAGPL-3.0
Updated11 hours ago
LanguagePython
Setup difficultyAdvanced
Stars
1 221
Ports
80 (nginx reverse proxy (HTTP_PORT)), 443 (nginx reverse proxy (HTTPS_PORT)), 5000 (API server, internal / behind proxy), 8080 (static file server for the web clients), 4321 (client-participation-alpha web client)
Requires
PostgreSQL
Install with Docker Compose

What is Polis?

Should you self-host Polis?

Polis scores 54/100 on selfhost.directory (how scoring works) and ranks #1 of 2 in Surveys. It has 1 221 GitHub stars.

It shipped 1 stable release in the past year. The latest, v2.42.2, came out on 25 October 2026.

Setup is advanced: plan for several services and some ongoing maintenance. It is AGPL-3.0-licensed and written mostly in Python.

Its closest alternative on selfhost.directory is Digistorm (54/100). Polis scores higher overall. See Polis vs Digistorm side by side.

Features

  • Admin Interface
  • AI-generated topic names
  • AI narrative reports
  • Automated opinion clustering
  • Automatic comment translationOptional; needs Google Cloud Translation credentials
  • Comment Moderation
  • Comment routing and prioritization
  • Consensus statement detection
  • Data Export
  • Embeddable conversations
  • Operations dashboardOff by default
  • Real-time opinion visualization
  • Real-time shareable report
  • Spam FilteringOptional; requires an Akismet API key
  • Statement-based group polling

Polis README

How to install Polis

Install Polis with Docker Compose

a recent docker (and Docker Desktop if you are on Mac or Windows)
cp example.env .env
docker compose --profile postgres --profile local-services up --build

Or, after generating JWT keys and local SSL certificates:

make generate-jwt-keys
cp example.env .env
make start

mkcert local certificates and JWT keys are one-time setup steps before starting; `make PROD start` runs the stack without the dev overlay for production.

How to configure Polis

  • ADMIN_EMAIL_DATA_EXPORT
  • ADMIN_EMAIL_DATA_EXPORT_TESTEMAIL ADDRESSES ######

    EMAIL ADDRESSES ######

  • ADMIN_EMAIL_EMAIL_TEST
  • ADMIN_EMAILSArray of email addresses to receive team notifications.

    Array of email addresses to receive team notifications.

  • ADMIN_UIDSGENERAL SETTINGS ######

    GENERAL SETTINGS ######

  • AKISMET_ANTISPAM_API_KEYTHIRD PARTY API CREDENTIALS ###### These are all optional, but some features will not work without them.

    THIRD PARTY API CREDENTIALS ###### These are all optional, but some features will not work without them.

  • ANTHROPIC_API_KEYOptional API keys for other services:

    Optional API keys for other services:

  • ANTHROPIC_MODEL
  • ANTHROPIC_TOPIC_MODELclaude-haiku-4-5-20251001Delphi topic-cluster naming model (Anthropic Batch API). Resolution order: ANTHROPIC_TOPIC_MODEL -> ANTHROPIC_MODEL -> claude-haiku-4-5-20251001

    Delphi topic-cluster naming model (Anthropic Batch API). Resolution order: ANTHROPIC_TOPIC_MODEL -> ANTHROPIC_MODEL -> claude-haiku-4-5-20251001

  • API_DEV_HOSTNAMElocalhostCUSTOM URL/HOSTNAME SETTINGS ###### If you are running a development server anywhere other than localhost:80 or localhost:5000, you need to update API_DEV_HOSTNAME and EMBED_SERVICE_HOSTNAME.

    CUSTOM URL/HOSTNAME SETTINGS ###### If you are running a development server anywhere other than localhost:80 or localhost:5000, you need to update API_DEV_HOSTNAME and EMBED_SERVICE_HOSTNAME.

  • API_PROD_HOSTNAMEpol.isIf you are running polis on a custom domain, set both API_PROD_HOSTNAME and DOMAIN_OVERRIDE to the same value. In the future these will be combined into one setting.

    If you are running polis on a custom domain, set both API_PROD_HOSTNAME and DOMAIN_OVERRIDE to the same value. In the future these will be combined into one setting.

  • API_SERVER_PORT5000PORTS ######

    PORTS ######

  • AUTH_AUDIENCEusersOIDC Config ######

    OIDC Config ######

  • AUTH_CERTS_PATH~/.simulacrum/certs
  • AUTH_CLIENT_IDdev-client-id
  • AUTH_CLIENT_SECRETdev_auth-client_secret
  • AUTH_DOMAINlocalhost:3000
  • AUTH_ISSUERhttps://localhost:3000/
  • AUTH_KEYS_PATH
  • AUTH_NAMESPACEhttps://pol.is/
  • AUTH_SIMULATOR_PORT3000
  • AWS_ACCESS_KEY_IDExplicit local emulator credentials. Omit both keys to use the SDK default chain (for example an EC2 instance role); application code supplies no fake keys.

    Explicit local emulator credentials. Omit both keys to use the SDK default chain (for example an EC2 instance role); application code supplies no fake keys.

  • AWS_REGIONus-east-1Read from process.env by aws-sdk https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html Declared ONCE, here. This single credential pair is shared by every AWS client in the server and delphi containers -- DynamoDB, S3/MinIO, SES and SQS -- so a second declaration further down this file would silently win (last one wins in a dotenv file) and han

    Read from process.env by aws-sdk https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html Declared ONCE, here. This single credential pair is shared by every AWS client in the server and delphi containers -- DynamoDB, S3/MinIO, SES and SQS -- so a second declaration further down this file would silently win (last one wins in a dotenv file) and han

  • AWS_S3_BUCKET_NAMEpolis-delphi
  • AWS_S3_ENDPOINTS3 STORAGE ###### MinIO configuration for local S3-compatible storage. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION are deliberately NOT repeated here -- they are declared once near the top of this file and are already set to the MinIO pair. Re-declaring them here would make the file order, rather than intent, decide which service's credentials the server and delphi containers end up usi

    S3 STORAGE ###### MinIO configuration for local S3-compatible storage. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION are deliberately NOT repeated here -- they are declared once near the top of this file and are already set to the MinIO pair. Re-declaring them here would make the file order, rather than intent, decide which service's credentials the server and delphi containers end up usi

  • AWS_SECRET_ACCESS_KEY
  • BACKFILL_COMMENT_LANG_DETECTIONThe following flags will all default to false if not set.

    The following flags will all default to false if not set.

  • CACHE_MATH_RESULTSBOOLEAN FLAGS ###### Instructs the API server to cache the results from the math service. Default is true if not set.

    BOOLEAN FLAGS ###### Instructs the API server to cache the results from the math service. Default is true if not set.

  • CDK_ENABLE_OLLAMACDK / INFRA ##### Recreate the (temporarily retired) Ollama GPU stack: ASG, GPU launch template, EFS, internal NLB, and the /polis/ollama-service-url secret. Default: off.

    CDK / INFRA ##### Recreate the (temporarily retired) Ollama GPU stack: ASG, GPU launch template, EFS, internal NLB, and the /polis/ollama-service-url secret. Default: off.

  • COMPOSE_PROJECT_NAMEpolis-${TAG}
  • DATABASE_SSLSet to `true` for some production environments:

    Set to `true` for some production environments:

  • DATABASE_URLAlways required. Replace with your own database URL if not using dockerized postgres.

    Always required. Replace with your own database URL if not using dockerized postgres.

  • DELPHI_RESULT_BACKENDDELPHI RESULT READERS ###### Unset means DynamoDB. Only an explicit postgres value selects the new reader. PostgreSQL needs M28/M29 and verified published/imported coverage first.

    DELPHI RESULT READERS ###### Unset means DynamoDB. Only an explicit postgres value selects the new reader. PostgreSQL needs M28/M29 and verified published/imported coverage first.

  • DELPHI_RESULT_ENV
  • DELPHI_RESULT_SCOPE
  • DELPHI_WRITER_CODE_SHARequired code provenance when admitting Postgres Delphi writers (40/64 hex).

    Required code provenance when admitting Postgres Delphi writers (40/64 hex).

  • DEV_MODEtrueSet to `false` for production:

    Set to `false` for production:

  • DOMAIN_OVERRIDE
  • DOMAIN_WHITELIST_ITEM_01
  • DOMAIN_WHITELIST_ITEM_02
  • DOMAIN_WHITELIST_ITEM_03
  • DOMAIN_WHITELIST_ITEM_04
  • DOMAIN_WHITELIST_ITEM_05
  • DOMAIN_WHITELIST_ITEM_06
  • DOMAIN_WHITELIST_ITEM_07
  • DOMAIN_WHITELIST_ITEM_08
  • DYNAMODB_ENDPOINThttp://host.docker.internal:8000DYNAMODB ###### When using local DynamoDB, this should be http://dynamodb:8000. In production leave it blank.

    DYNAMODB ###### When using local DynamoDB, this should be http://dynamodb:8000. In production leave it blank.

  • EMBED_SERVICE_HOSTNAMElocalhostIn production, should match API_PROD_HOSTNAME or DOMAIN_OVERRIDE, if set:

    In production, should match API_PROD_HOSTNAME or DOMAIN_OVERRIDE, if set:

  • ENCRYPTION_PASSWORD_00001DEPRECATED ###### (Deprecated) Used internally by Node.Crypto to encrypt/decrypt IP addresses.

    DEPRECATED ###### (Deprecated) Used internally by Node.Crypto to encrypt/decrypt IP addresses.

  • GA_TRACKING_IDTo enable Google Analytics, set this to your tracking id:

    To enable Google Analytics, set this to your tracking id:

  • GEMINI_API_KEY
  • GIT_HASHOptionally set this manually or use `git rev-parse HEAD`. It can be useful for debugging.

    Optionally set this manually or use `git rev-parse HEAD`. It can be useful for debugging.

  • GOOGLE_APPLICATION_CREDENTIALSThis value is written by the server app if SHOULD_USE_TRANSLATION_API is true.

    This value is written by the server app if SHOULD_USE_TRANSLATION_API is true.

  • GOOGLE_CREDENTIALS_BASE64One of the following is required if SHOULD_USE_TRANSLATION_API is true:

    One of the following is required if SHOULD_USE_TRANSLATION_API is true:

  • GOOGLE_CREDS_STRINGIFIED
  • HTTP_PORT80
  • HTTPS_PORT443
  • INSTANCE_SIZEdevDELPHI INSTANCE SIZE ###### Should be dev (process all jobs), large (process large jobs only), normal or small (only normal jobs). Leave empty for autodetection on AWS deployment. See delphi/DELPHI_AUTOSCALING_SETUP.md for configuring instance size in production.

    DELPHI INSTANCE SIZE ###### Should be dev (process all jobs), large (process large jobs only), normal or small (only normal jobs). Leave empty for autodetection on AWS deployment. See delphi/DELPHI_AUTOSCALING_SETUP.md for configuring instance size in production.

  • INTERNAL_SERVICE_URLhttp://host.docker.internal:5000/api/v3CLIENT PARTICIPATION ALPHA (optional) ######

    CLIENT PARTICIPATION ALPHA (optional) ######

  • JWKS_URIhttps://host.docker.internal:3000/.well-known/jwks.json
  • JWT_PRIVATE_KEYfor production:

    for production:

  • JWT_PRIVATE_KEY_PATH
  • JWT_PUBLIC_KEY
  • JWT_PUBLIC_KEY_PATH
  • LLM_PROVIDERanthropicLLM PROVIDER (Delphi topic naming) ##### anthropic (default, Batch API) or ollama (self-hosted GPU).

    LLM PROVIDER (Delphi topic naming) ##### anthropic (default, Batch API) or ollama (self-hosted GPU).

  • LOCAL_SERVICES_DOCKERtrueMakefile will read this to determine if local services (dynamodb, minio, ollama) should be started.

    Makefile will read this to determine if local services (dynamodb, minio, ollama) should be started.

  • LOGIN_CODE_PEPPERchange-this-in-prodTreevite login code lookup pepper (used for SHA-256 lookup hash)

    Treevite login code lookup pepper (used for SHA-256 lookup hash)

  • MAILGUN_API_KEY
  • MAILGUN_DOMAIN
  • MATH_CAPACITY_KEEP_FRACTION
  • MATH_CAPACITY_LARGE_BUDGET_MBThe large class's compute budget in MiB (at most 0.85 of the large box's container memory); unset: nothing is reported as exceeding it. The queue child refuses a job when this exceeds its own budget.

    The large class's compute budget in MiB (at most 0.85 of the large box's container memory); unset: nothing is reported as exceeding it. The queue child refuses a job when this exceeds its own budget.

  • MATH_CAPACITY_PROMOTE1 (needs ROUTING=1): promote the staged `python-large` bundles into this label.

    1 (needs ROUTING=1): promote the staged `python-large` bundles into this label.

  • MATH_CAPACITY_QUEUE_DSNThe job queue (needs ROUTING=1 to matter): a libpq DSN for a login that is a member of polis_queue_executor (never the DATABASE_URL login), and the queue env namespace the jobs are inserted into and counted from. Unset: routed conversations are not enqueued.

    The job queue (needs ROUTING=1 to matter): a libpq DSN for a login that is a member of polis_queue_executor (never the DATABASE_URL login), and the queue env namespace the jobs are inserted into and counted from. Unset: routed conversations are not enqueued.

  • MATH_CAPACITY_QUEUE_ENV
  • MATH_CAPACITY_RESIZE_S
  • MATH_CAPACITY_RESTAGEA 16-64 lowercase-hex nonce marks every routed conversation for one rebuild; remove it after use.

    A 16-64 lowercase-hex nonce marks every routed conversation for one rebuild; remove it after use.

  • MATH_CAPACITY_ROUTE_FRACTION
  • MATH_CAPACITY_ROUTINGLARGE MEMORY CLASS (P-073) ###### Off unless set. With MATH_CAPACITY_ROUTING unset, math-python computes and publishes exactly as before and nothing below has any effect. The large class has no resident worker: an oversized conversation becomes one math_rebuild job of worker class `large` on the Postgres job queue, and the large box's polis-jobs daemon (POLIS_JOBS_WORKER_CLASS=large) runs `scripts

    LARGE MEMORY CLASS (P-073) ###### Off unless set. With MATH_CAPACITY_ROUTING unset, math-python computes and publishes exactly as before and nothing below has any effect. The large class has no resident worker: an oversized conversation becomes one math_rebuild job of worker class `large` on the Postgres job queue, and the large box's polis-jobs daemon (POLIS_JOBS_WORKER_CLASS=large) runs `scripts

  • MATH_CAPACITY_STATE_PATHOptional: keep the capacity records across restarts (on the state volume).

    Optional: keep the capacity records across restarts (on the state volume).

  • MATH_CONV_CACHE_CAPIn-memory conversation cache: max conversations before LRU-evicting the coldest. FINITE by default (200) — an unbounded cache is NOT acceptable for a long shadow soak (Clojure's 4h reboot was the de-facto cap, which the Python poller dropped). Set 0 to opt into UNLIMITED (documented but discouraged); a negative value is rejected at startup. Choose a value from measured cache churn / container memo

    In-memory conversation cache: max conversations before LRU-evicting the coldest. FINITE by default (200) — an unbounded cache is NOT acceptable for a long shadow soak (Clojure's 4h reboot was the de-facto cap, which the Python poller dropped). Set 0 to opt into UNLIMITED (documented but discouraged); a negative value is rejected at startup. Choose a value from measured cache churn / container memo

  • MATH_ENVdevOptions: prod, preprod, dev, python. The label the readers (server, delphi) serve. Production serves `python`; `prod` holds the retired Clojure engine's rows. The dev overlay's math-python writes this label unless MATH_PYTHON_ENV is set.

    Options: prod, preprod, dev, python. The label the readers (server, delphi) serve. Production serves `python`; `prod` holds the retired Clojure engine's rows. The dev overlay's math-python writes this label unless MATH_PYTHON_ENV is set.

  • MATH_POLLER_ALLOW_SERVED_ENVThe poller refuses to start with an empty math_env, or with math_env=prod (the served namespace) unless this is set to 1. Set it only for a deliberate cut-over.

    The poller refuses to start with an empty math_env, or with math_env=prod (the served namespace) unless this is set to 1. Set it only for a deliberate cut-over.

  • MATH_POLLER_DUMP_DIRerrorconv dump dir + retries before parking a failing zid. Defaults scratch/errorconv, 1

    errorconv dump dir + retries before parking a failing zid. Defaults scratch/errorconv, 1

  • MATH_POLLER_RECONCILE_INTERVAL_MSParked-zid reconciler cadence in ms. A background pass recovers a zid that failed, was parked, and then received no further votes by rebuilding it from authoritative Postgres history. Default 60000 (60s).

    Parked-zid reconciler cadence in ms. A background pass recovers a zid that failed, was parked, and then received no further votes by rebuilding it from authoritative Postgres history. Default 60000 (60s).

  • MATH_POLLER_RETRY_CAP
  • MATH_PYTHON_ENVPYTHON MATH POLLER (math-python) ###### The math engine (it replaced the retired Clojure `math` container). Writes math_main/math_bidtopid/math_ptptstats under its own math_env. docker-compose.yml gates it behind --profile math-python; the dev overlay runs it by default. See delphi/docs/MATH_POLLER_DESIGN.md. math_env the poller writes under. Keep `python`: the cutover moves the readers (MATH_ENV=

    PYTHON MATH POLLER (math-python) ###### The math engine (it replaced the retired Clojure `math` container). Writes math_main/math_bidtopid/math_ptptstats under its own math_env. docker-compose.yml gates it behind --profile math-python; the dev overlay runs it by default. See delphi/docs/MATH_POLLER_DESIGN.md. math_env the poller writes under. Keep `python`: the cutover moves the readers (MATH_ENV=

  • MATH_WORKER_POOL_SIZEConversations processed concurrently (each zid stays serialized). Default 4

    Conversations processed concurrently (each zid stays serialized). Default 4

  • MAX_REPORT_CACHE_DURATIONA value in miliseconds for caching AI responses for narrativeReport

    A value in miliseconds for caching AI responses for narrativeReport

  • OIDC_CACHE_KEY_ID_TOKEN_SUFFIX
  • OIDC_CACHE_KEY_PREFIX
  • OLLAMA_HOSThttp://host.docker.internal:11434OLLAMA ##### Only used when LLM_PROVIDER=ollama. The GPU infra is OFF by default; re-enable it in CDK by deploying with CDK_ENABLE_OLLAMA=true, then set LLM_PROVIDER=ollama.

    OLLAMA ##### Only used when LLM_PROVIDER=ollama. The GPU infra is OFF by default; re-enable it in CDK by deploying with CDK_ENABLE_OLLAMA=true, then set LLM_PROVIDER=ollama.

  • OLLAMA_MODELllama3.1:8b
  • OPENAI_API_KEY
  • OPS_COST_EXPLORER
  • OPS_DATA_SOURCEThe system pages' AWS reads (CloudWatch, Auto Scaling, CodeDeploy) with the EC2 instance role. Off unless `aws`; locally there is no instance role, so leave it unset. OPS_COST_EXPLORER=1 adds the Cost page (Cost Explorer bills $0.01 per request; at most one read, of one or two requests, per 12 hours per server process).

    The system pages' AWS reads (CloudWatch, Auto Scaling, CodeDeploy) with the EC2 instance role. Off unless `aws`; locally there is no instance role, so leave it unset. OPS_COST_EXPLORER=1 adds the Cost page (Cost Explorer bills $0.01 per request; at most one read, of one or two requests, per 12 hours per server process).

  • OPS_EMAIL_DOMAINSpolis.test,!moderator@polis.testWho may open them (read only when OPS_ENABLED=true). Comma-separated: a domain (example.org), an exact address (someone@example.org), or either prefixed with `!` to deny. Locally every simulator user passes except the denied moderator. polis.test is refused unless DEV_MODE=true. See docs/configuration.md.

    Who may open them (read only when OPS_ENABLED=true). Comma-separated: a domain (example.org), an exact address (someone@example.org), or either prefixed with `!` to deny. Locally every simulator user passes except the denied moderator. polis.test is refused unless DEV_MODE=true. See docs/configuration.md.

  • OPS_ENABLEDOPERATIONS PAGES (/ops in the admin console) ###### Off unless `true`: every /api/v3/ops route answers 404 and no link is shown.

    OPERATIONS PAGES (/ops in the admin console) ###### Off unless `true`: every /api/v3/ops route answers 404 and no link is shown.

  • OPS_MIN_VOTERS_FOR_TEXTConversations need this many distinct voters in the last 7 days before the topics and consensus pages show their topic or statement text. Default 20.

    Conversations need this many distinct voters in the last 7 days before the topics and consensus pages show their topic or statement text. Default 20.

  • PARTICIPATION_ACQUIRE_TIMEOUT_MS
  • PARTICIPATION_POOL_MAXThe API server's participation pools, used by votes, comments, the next statement and participationInit and nothing else (docs/configuration.md "Connection budget"). Sizes default to the read-write and read pools' (12 in production, 2 in dev). A participation query waits for a connection; the acquire bound (ms, 0 = none) only surfaces a database that stopped answering.

    The API server's participation pools, used by votes, comments, the next statement and participationInit and nothing else (docs/configuration.md "Connection budget"). Sizes default to the read-write and read pools' (12 in production, 2 in dev). A participation query waits for a connection; the acquire bound (ms, 0 = none) only surfaces a database that stopped answering.

  • PARTICIPATION_READ_POOL_MAX
  • POLIS_FROM_ADDRESS
  • POLIS_JWT_AUDIENCEparticipantsIn-house JWT Configuration (for XID and anonymous participants)

    In-house JWT Configuration (for XID and anonymous participants)

  • POLIS_JWT_ISSUERhttps://pol.is/
  • POLL_ALLOWLISTOptional zid filters (comma-separated). Aliases: MATH_ZID_ALLOWLIST/MATH_ZID_BLOCKLIST

    Optional zid filters (comma-separated). Aliases: MATH_ZID_ALLOWLIST/MATH_ZID_BLOCKLIST

  • POLL_BLOCKLIST
  • POLL_FROM_DAYS_AGOWatermark boot window: start polling from N days ago. Default 10

    Watermark boot window: start polling from N days ago. Default 10

  • POLL_MODERATION_OVERLAP_MSModeration poll overlap in ms: the same for comments.modified, so a moderation change that commits after a later-stamped change was polled is still delivered (once). 0 = the strict watermark. Default 60000

    Moderation poll overlap in ms: the same for comments.modified, so a moderation change that commits after a later-stamped change was polled is still delivered (once). 0 = the strict watermark. Default 60000

  • POLL_MOD_INTERVAL_MS
  • POLL_SHARD_COUNT
  • POLL_SHARD_INDEXzid-sharding across independent PROCESSES (one shard = one process; the pool's threads do not parallelise this CPU-bound workload). Default unsharded (index 0, count 1). Before scaling to N replicas, give each a UNIQUE index in [0, N) and set count=N on all of them, else every shard duplicates every computation. An out-of-range index is fatal at startup (a silent no-zid shard is the worst mode).

    zid-sharding across independent PROCESSES (one shard = one process; the pool's threads do not parallelise this CPU-bound workload). Default unsharded (index 0, count 1). Before scaling to N replicas, give each a UNIQUE index in [0, N) and set count=N on all of them, else every shard duplicates every computation. An out-of-range index is fatal at startup (a silent no-zid shard is the worst mode).

  • POLL_VOTE_INTERVAL_MSPoll cadences in ms. Defaults 1000/1000

    Poll cadences in ms. Defaults 1000/1000

  • POLL_VOTE_OVERLAP_MSVote poll overlap in ms: each vote poll also reads this far below its watermark, so a vote that commits after a later-stamped vote was polled is still delivered (once). 0 = the strict watermark. Default 60000

    Vote poll overlap in ms: each vote poll also reads this far below its watermark, so a vote that commits after a later-stamped vote was polled is still delivered (once). 0 = the strict watermark. Default 60000

  • POSTGRES_CONNECT_TIMEOUT5Delphi's Postgres connect timeout in seconds. Default (when this is unset) is 30s, which is a conservative production value. Local dev typically wants fast-fail so a missing/down Postgres surfaces in seconds rather than the OS default (~60-120s). See delphi/CLAUDE.md "Environment Variables".

    Delphi's Postgres connect timeout in seconds. Default (when this is unset) is 30s, which is a conservative production value. Local dev typically wants fast-fail so a missing/down Postgres surfaces in seconds rather than the OS default (~60-120s). See delphi/CLAUDE.md "Environment Variables".

  • POSTGRES_DBpolis-devRequired for using dockerized postgres (ie --profile postgres).

    Required for using dockerized postgres (ie --profile postgres).

  • POSTGRES_DOCKERtrueMakefile will read this to determine if the database is running in a docker container.

    Makefile will read this to determine if the database is running in a docker container.

  • POSTGRES_HOSTpostgres:${POSTGRES_PORT}
  • POSTGRES_PASSWORD
  • POSTGRES_PORT5432
  • POSTGRES_USERpostgres
  • PUBLIC_SERVICE_URLhttp://localhost/api/v3
  • READ_ONLY_DATABASE_URLDATABASE ###### Optional DB replica for reads:

    DATABASE ###### Optional DB replica for reads:

  • RUN_PERIODIC_EXPORT_TESTS
  • SENTENCE_TRANSFORMER_MODELall-MiniLM-L6-v2SENTENCE TRANSFORMER ###### Default model for English text (384-dimensional embeddings)

    SENTENCE TRANSFORMER ###### Default model for English text (384-dimensional embeddings)

  • SERVER_ENV_FILE.envOptionally give the server container a distinct env_file. Useful for CI tests.

    Optionally give the server container a distinct env_file. Useful for CI tests.

  • SERVER_LOG_LEVELUsed by winston via server/utils/logger. Defaults to "warn".

    Used by winston via server/utils/logger. Defaults to "warn".

  • SERVER_LOG_TO_FILEWhen true, logs are written to server/logs in addition to stdout. If docker compose is used, the logs directory is mounted as a persistent volume.

    When true, logs are written to server/logs in addition to stdout. If docker compose is used, the logs directory is mounted as a persistent volume.

  • SERVICE_URLFull hostname with protocol of your API server: (Optional. Currently only used in client-report development.)

    Full hostname with protocol of your API server: (Optional. Currently only used in client-report development.)

  • SES_ENDPOINThttp://localhost:8005
  • SHOULD_USE_TRANSLATION_API
  • SIMPLE_ANALYTICS_API_KEY"Where visitors come from": a Simple Analytics API key. Unset: the page says so and reads nothing. SIMPLE_ANALYTICS_HOSTNAME defaults to pol.is.

    "Where visitors come from": a Simple Analytics API key. Unset: the page says so and reads nothing. SIMPLE_ANALYTICS_HOSTNAME defaults to pol.is.

  • SIMPLE_ANALYTICS_HOSTNAME
  • SQS_LOCAL_ENDPOINT
  • SQS_QUEUE_URLhttp://localstack:4566/000000000000/import-jobs-queue
  • STATIC_FILES_ADMIN_PORT${STATIC_FILES_PORT}These ports could actually be different in development or if you are hosting clint-admin, client-participation, and file-server on different servers or containers.

    These ports could actually be different in development or if you are hosting clint-admin, client-participation, and file-server on different servers or containers.

  • STATIC_FILES_HOSTfile-server
  • STATIC_FILES_PARTICIPATION_PORT${STATIC_FILES_PORT}
  • STATIC_FILES_PORT8080
  • TAGdevDOCKER CONCERNS ###### Optionally set this to whatever you want. It is a convenience for docker assets.

    DOCKER CONCERNS ###### Optionally set this to whatever you want. It is a convenience for docker assets.

  • TOPICAL_COMMENT_RATIO0.6Decimal between 0.0 and 1.0. This determines how frequently to pull the next comment from selected topics, if available.

    Decimal between 0.0 and 1.0. This determines how frequently to pull the next comment from selected topics, if available.

  • USE_PRODCLONEfalsePostgreSQL initialization mode (only applies when POSTGRES_DOCKER=true) false (default): Use migrations from server/postgres/migrations/*.sql (clean dev setup) true: Restore from prodclone.dump file (must exist in project root directory) Note: Each mode uses a separate Docker volume for easy switching without data loss

    PostgreSQL initialization mode (only applies when POSTGRES_DOCKER=true) false (default): Use migrations from server/postgres/migrations/*.sql (clean dev setup) true: Restore from prodclone.dump file (must exist in project root directory) Note: Each mode uses a separate Docker volume for easy switching without data loss

  • WEBSERVER_PASSws-pass(Deprecated) Basic Auth settings for certain requests between math and api services.

    (Deprecated) Basic Auth settings for certain requests between math and api services.

  • WEBSERVER_USERNAMEws-user

Polis releases & version history

FAQ

How much is automatic? How much is required of people?

Once a prompt and some seed comments are set, a conversation can run fully automatically.

Are comments sent out randomly?

No — comments are routed semi-randomly, prioritising more divisive statements because they help form opinion groups.

Does Polis work in multiple languages?

Yes — the participation interface has been translated into several languages.

Does Polis scale? To what degree?

Yes; the largest conversation run so far had over 33,000 participants producing nearly 2,000,000 votes on about 1,000 comments.

Is the interface complicated for participants?

No — participants can only submit a comment or vote on comments submitted by others.

Does Polis visualize the debate as it's ongoing? Is this just for the conversation owner or for all participants?

Yes; the report updates in real time and is available to the conversation owner through the admin interface, using long-link privacy so it can be shared.

Does Polis use natural language processing (NLP)?

No — the machine-learning algorithms run only on the opinion matrix built from votes, not on the comment text.

Can I deploy my own instance of Polis? How easy is it to do that?

People have done it, but the project says it is still not easy and needs senior engineers comfortable with cloud systems.

Is Polis open source?

Yes — the repository holds the platform, and the data-science analysis notebooks are open source too.

Can I embed Polis on my site?

Yes, embedding it on your own site is the preferred way to build trust with participants.

What is Polis not?

Not a tool for picking the best idea or comparing ideas head to head, not for demographic questions, and not meant for groups smaller than roughly 50 to 100 people.

Rate this project

Click to rate — no account needed.

0 comments

Posting as guest
Loading comments…