You can run everything with Docker, or run the Python services locally with hot reload against Dockerized infrastructure.
docker compose up -d
# Health check
curl -sf http://localhost:5000/health && echo API OK- Start infra-only services in Docker:
docker compose up -d postgres s3mock smtp redis status pgadmin- In one terminal, run the API:
export DUO_ENV=dev
export DUO_DB_HOST=localhost
export DUO_DB_PORT=5432
export DUO_DB_USER=postgres
export DUO_DB_PASS=password
export DUO_CORS_ORIGINS='*'
export DUO_R2_BUCKET_NAME=s3-mock-bucket
export DUO_R2_AUDIO_BUCKET_NAME=s3-mock-audio-bucket
export DUO_R2_ACCT_ID=dev
export DUO_R2_ACCESS_KEY_ID=s3-mock-access-key-id
export DUO_R2_ACCESS_KEY_SECRET=s3-mock-secret-access-key-secret
export DUO_BOTO_ENDPOINT_URL=http://localhost:9090
export DUO_SMTP_HOST=localhost
export DUO_SMTP_PORT=1025
./api.main.sh- In another terminal, run the Chat service:
export DUO_ENV=dev
export DUO_DB_HOST=localhost
export DUO_DB_PORT=5432
export DUO_DB_USER=postgres
export DUO_DB_PASS=password
export DUO_R2_AUDIO_BUCKET_NAME=s3-mock-audio-bucket
export DUO_R2_ACCT_ID=dev
export DUO_R2_ACCESS_KEY_ID=s3-mock-access-key-id
export DUO_R2_ACCESS_KEY_SECRET=s3-mock-secret-access-key-secret
export DUO_BOTO_ENDPOINT_URL=http://localhost:9090
export DUO_CHAT_PORTS=5443
./chat.main.shNotes:
- OTPs are
000000for@example.comemails indev. - Redis must be reachable at
redis://redis:6379. The default Docker Composeredisservice exposes this.
- Create a test user (on a running API):
./test/util/create-user.sh alice 30 1 true- Run a single functionality test:
./test/util/with-container.sh ./test/functionality1/status.sh- Run a whole suite:
./test/util/with-container.sh ./test/functionality.sh 1./mypy.sh # check core modules
./mypy.sh path.py # check a specific file or directoryDUO_ENV- Should be set toprodfor production deployments. Setting this toproddisables the ability to sign up with an OTP of 000000 by using an @example.com email address.
These environment variables let the api container know where your SMTP server is and how to log into it:
DUO_SMTP_HOST- Your SMTP server's hostname. Might be something like email-smtp.us-west-1.amazonaws.com if you're using AWS SES.DUO_SMTP_PORT- Your SMTP server's port.DUO_SMTP_USER- Your SMTP server's username.DUO_SMTP_PASS- Your SMTP server's password.
The api container uses the SMTP server to sent one-time passwords to users who want to sign up or log in.
These environment variables let the api container know where your PostgreSQL database is:
DUO_DB_HOST- Your PostgreSQL database's hostname.DUO_DB_PORT- Your PostgreSQL database's port.DUO_DB_USER- Your PostgreSQL database's username.DUO_DB_PASS- Your PostgreSQL database's password.
This environment variable allows the server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources:
DUO_CORS_ORIGINS- Defaults to '*' if not set.
These environment variables specify where user-uploaded content is stored:
DUO_R2_BUCKET_NAME- Refers to the bucket where user-uploaded images are stored.DUO_R2_AUDIO_BUCKET_NAME- Refers to the bucket where user-uploaded audio is stored.DUO_R2_ACCT_ID- Your account ID. This is assumed to be the same for both buckets (i.e. audio and images).DUO_R2_ACCESS_KEY_ID- Your access key ID. This is assumed to be the same for both buckets (i.e. audio and images).DUO_R2_ACCESS_KEY_SECRET- Your access key secret. This is assumed to be the same for both buckets (i.e. audio and images).DUO_BOTO_ENDPOINT_URL- Your endpoint URL. This defaults tohttps://{R2_ACCT_ID}.r2.cloudflarestorage.comif unset.
These env vars get passed to the boto3 library, so they're compatible with AWS S3 despite containing R2 in their names. The api container needs to have permissions to upload files to these buckets. Deletion is handled by the cron container.
These environment variables let the chat container know where your PostgreSQL database is:
DUO_DB_HOST- Your PostgreSQL database's hostname.DUO_DB_PORT- Your PostgreSQL database's port.DUO_DB_USER- Your PostgreSQL database's username.DUO_DB_PASS- Your PostgreSQL database's password.
This environment variable determines which port, or ports, workers operate on:
DUO_CHAT_PORTS- This could be a single number (e.g.5443) or a range (e.g.5443-5447). Specifying a range starts a worker for each port.
If you use more than one worker, you need to place a load balancer between the chat container and clients.
These environment variables let the cron container know where your SMTP server is and how to log into it:
DUO_SMTP_HOST- Your SMTP server's hostname. Might be something like email-smtp.us-west-1.amazonaws.com if you're using AWS SES.DUO_SMTP_PORT- Your SMTP server's port.DUO_SMTP_USER- Your SMTP server's username.DUO_SMTP_PASS- Your SMTP server's password.
The cron container uses the SMTP server to sent message notifications, as well as notifications that a user's account has been deactivated due to inactivity.
These environment variables let the cron container know where your PostgreSQL database is:
DUO_DB_HOST- Your PostgreSQL database's hostname.DUO_DB_PORT- Your PostgreSQL database's port.DUO_DB_USER- Your PostgreSQL database's username.DUO_DB_PASS- Your PostgreSQL database's password.
These environment variables specify where user-uploaded content is stored:
DUO_R2_BUCKET_NAME- Refers to the bucket where user-uploaded images are stored.DUO_R2_AUDIO_BUCKET_NAME- Refers to the bucket where user-uploaded audio is stored.DUO_R2_ACCT_ID- Your account ID. This is assumed to be the same for both buckets (i.e. audio and images).DUO_R2_ACCESS_KEY_ID- Your access key ID. This is assumed to be the same for both buckets (i.e. audio and images).DUO_R2_ACCESS_KEY_SECRET- Your access key secret. This is assumed to be the same for both buckets (i.e. audio and images).DUO_BOTO_ENDPOINT_URL- Your endpoint URL. This defaults tohttps://{R2_ACCT_ID}.r2.cloudflarestorage.comif unset.
These env vars get passed to the boto3 library, so they're compatible with AWS S3 despite containing R2 in their names. The api container needs to have permissions to upload files to these buckets. Deletion is handled by the cron container.
OPENAI_API_KEY- The OpenAI API key used to query ChatGPT while verifying accounts.
The api container requires a Redis instance accessible via redis://redis:6379. This address is currently hardcoded, here.
Note also that X-Forwarded-For headers are treated as the user's real IP by
Duolicious, which assumes that there's a proxy between it and users.
If there's no proxy, X-Forwarded-For headers can be spoofed by users. This
will allow malicious users to partially bypass rate limits and bans.
Whether X-Forwarded-For is used or not should probably be configurable in
Duolicious, but it's currently not. Although hardcoding the solution isn't too
hard: Simply remove the use of werkzeug.middleware.proxy_fix.ProxyFix.
Install these:
- Docker Compose
- jq
- curl
- ffmpeg
Then run this:
# Where ${n} is the test you want to run
./test/util/with-container.sh ./test/functionality${n}.shYou can also do this in one terminal:
docker compose up...Then run this in another:
# Where ${n} is the test you want to run
DUO_DB_PORT=5432 ./test/functionality${n}.sh~/duolicious-backend % sudo docker exec -it $(sudo docker ps | grep duolicious-backend-postgres | cut -d ' ' -f 1) psql -U postgres -d duo_api
[sudo] password for user:
psql (15.3 (Debian 15.3-1.pgdg120+1))
Type "help" for help.
duo_api=# CREATE EXTENSION pg_stat_statements;
duo_api=# select left(query, 100), mean_exec_time, calls from pg_stat_statements order by total_exec_time desc;
duo_api=# select pg_stat_statements_reset();
Terminal A:
docker compose down --remove-orphans
docker compose up postgresTerminal B:
pg_dump -h ${DB_HOST} -U postgres -d duo_chat -f /tmp/duo_chat.sql
pg_dump -h ${DB_HOST} -U postgres -d duo_api -f /tmp/duo_api.sql
PGPASSWORD=password psql -U postgres -h localhost -p 5432 -c 'create database duo_api;'
PGPASSWORD=password psql -U postgres -h localhost -p 5432 -c 'create database duo_chat;'
PGPASSWORD=password psql -U postgres -d duo_api -h localhost -p 5432 < /tmp/duo_api.sql
PGPASSWORD=password psql -U postgres -d duo_chat -h localhost -p 5432 < /tmp/duo_chat.sqlTerminal A:
^C
docker compose up- /var/lib/postgresql/data/postgresql.conf
- /var/lib/postgresql/data/log/*
- /etc/postgresql/16/main/postgresql.conf
- /var/lib/postgresql/16/main/postgresql.auto.conf
- /var/log/postgresql/postgresql-16-main.log