Configurable daily research digests powered by Codex.
This project automatically watches fresh papers from arXiv and OpenAlex every day, dedupes them, lets Codex decide what is actually worth reading, and sends polished HTML email digests to one or more recipients.
The hosted GitHub Actions workflow uses a dedicated Codex ChatGPT login. Do not upload your normal ~/.codex/auth.json: Codex refresh tokens are single-use, so sharing one token family between your laptop and GitHub-hosted runners will eventually invalidate one side.
A live sample digest is published at larstalian.github.io/send-me-research.
You can run it in two modes:
- one default digest from
.env - multiple custom digests with different recipients and different research priorities
Out of the box, the default audience is:
- LLMs
- agents
- robotics / embodied AI
- cyber / AI security
with extra weight on post-training, fine-tuning, code generation, tool use, and agentic-task improvement. You can keep that default, or define completely different stacks per recipient in digest_profiles.example.json or the Multiple People, Different Stacks section below.
- Install
uvand the Codex CLI. - Run
codex login. - Copy
.env.exampleto.env. - Fill in your SMTP settings.
- Install deps:
uv syncUse Gmail with an app password, not your normal password.
If you only use .env, the app sends one digest to EMAIL_TO using the built-in default audience:
- LLMs
- agents
- robotics
- cyber / security
- extra weight on post-training, fine-tuning, code generation, tool use, and agentic-task improvement
That is the fastest path if this is mainly for you.
Create digest_profiles.json from digest_profiles.example.json.
Each profile can define:
namerecipientsdescriptionpriority_keywordstop_n
Example:
{
"profiles": [
{
"name": "Applied ML",
"recipients": ["ml@example.com"],
"description": "LLMs, agents, post-training, code generation, and evaluation.",
"priority_keywords": ["fine-tuning", "post-training", "code generation"],
"top_n": 15
},
{
"name": "Security",
"recipients": ["security@example.com"],
"description": "Prompt injection, jailbreaks, agent security, and AI red teaming.",
"priority_keywords": ["prompt injection", "jailbreak", "red teaming"],
"top_n": 10
}
]
}If digest_profiles.json is present, the app sends one digest per profile. If it is absent, the app falls back to the default single profile from .env.
Pick a date:
TARGET_DATE="$(TZ=America/Los_Angeles date +%F)"Load env:
set -a
source .env
set +aPreview:
uv run send-me-research preview-digest --date "$TARGET_DATE"Send:
uv run send-me-research run-digest --date "$TARGET_DATE" --sendPreview one profile only:
uv run send-me-research preview-digest --date "$TARGET_DATE" --profile SecuritySee resolved profiles:
uv run send-me-research list-profilesRun tests:
uv run pytest -qAlways required:
EMAIL_FROMSMTP_HOSTSMTP_PORTSMTP_USERNAMESMTP_PASSWORD
Only required when you are not using profile config:
EMAIL_TO
Common optional settings:
DIGEST_TIMEZONECODEX_ENABLE_SEARCHCODEX_ENABLE_WILDCARD_DISCOVERYTOP_NCODEX_SHORTLIST_SIZEARXIV_MAX_RESULTSARXIV_CATEGORIESROBOTICS_SPOTLIGHT_COUNT
Two workflows are included:
.github/workflows/daily-digest-hosted.yml.github/workflows/daily-digest.yml
Hosted mode is the recommended setup if you want:
- a public repo
- no daily commits for state
- no always-on self-hosted runner
Set these GitHub secrets:
CODEX_AUTH_JSON_BASE64CODEX_CONFIG_TOML_BASE64CODEX_SECRET_SYNC_TOKEN, required so hosted runs can persist refreshed Codex auth back to GitHub SecretsEMAIL_FROMSMTP_HOSTSMTP_PORTSMTP_USERNAMESMTP_PASSWORDEMAIL_TOoptionalDIGEST_PROFILES_JSONoptionalOPENALEX_MAILTOoptional
Best path:
./scripts/sync_github_hosted_secrets.shThat script:
- starts a fresh hosted-only Codex device login
- uploads that temporary Codex auth
- deletes the local temporary auth copy
- uploads mail secrets from
.env - uploads
digest_profiles.jsonasDIGEST_PROFILES_JSONif the file exists - sets
DIGEST_AUTOMATION_MODE=hosted - refuses to sync if the hosted-only Codex login cannot pass a real
codex execprobe
If DIGEST_PROFILES_JSON is set, the workflow uses that instead of a checked-in digest_profiles.json. That is the cleanest way to keep recipient emails and audience configs private in a public repo.
Create a repo-scoped GitHub token that can update Actions secrets and put it in .env as CODEX_SECRET_SYNC_TOKEN before running the sync script. The workflow refuses to consume Codex auth unless that token is present, and it writes the refreshed ~/.codex/auth.json back after every Codex attempt, including failed digest runs.
The workflows do not commit state back to git. They restore the latest state artifact, use it for dedupe, prune it, and upload a fresh artifact after each run.
Local scratch paths:
state/out/
Neither is tracked in git.