{"slug": "a-free-ai-news-briefing-agent-that-runs-on-github-actions-no-server", "title": "A free AI news briefing agent that runs on GitHub Actions, no server", "summary": "A free AI news briefing agent that runs entirely on GitHub Actions has been released, requiring no server and using two free API keys from Groq and Tavily. The tool, available at github.com/tballochi/daily-briefing, autonomously researches user-selected topics, writes summaries from real search results, and emails a daily digest, with a dry-run mode that sends nothing and stores nothing.", "body_md": "**Wake up to an AI-written news briefing in your inbox every morning, on the\ntopics you choose, written from real freshly-searched articles.100% free, no\nserver, runs entirely on GitHub Actions.**\n\n*A real briefing. Every headline, summary and link in it was\nchosen and written by the agent.*\n\nIt's 8am. Your coffee is still too hot to drink, so you open your inbox, and the only thing worth reading is already there. Three stories that actually matter to you, four sentences each, every source linked and dated. Nothing you were sent yesterday. No feed, no notifications, no twenty tabs you'll never get back to.\n\nWhile you were asleep, an agent searched the web on your topics, threw out the filler, read what was left and wrote it up. By the time your coffee is drinkable, you're caught up, and you close the tab and get on with your day.\n\nYou need **two free API keys**. No Gmail, no deploy, no account anywhere else.\n\n```\ngit clone https://github.com/tballochi/daily-briefing.git\ncd daily-briefing\npip install -r requirements.txt\n\npython main.py --setup      # asks for your keys and checks each one works\npython main.py --dry-run    # builds a real briefing, sends nothing\n```\n\nBoth keys are free: [Groq](https://console.groq.com/keys) and\n[Tavily](https://app.tavily.com). Prefer not to use the wizard? Put them in a `.env`\n\nfile yourself as `GROQ_API_KEY=`\n\nand `TAVILY_API_KEY=`\n\n.\n\n`--dry-run`\n\nresearches the news, writes the briefing, prints it to your terminal and\nsaves the rendered email to `data/preview.html`\n\n. **It sends nothing and stores nothing**,\nso you can see exactly what you'd be getting before setting up delivery.\n\nThen edit `config.yaml`\n\nto your own topics and run it again.\n\nEvery morning, an autonomous agent:\n\n**Researches.** Runs live web searches across*your*topics, judges the results, and skips anything it already sent you on a previous day.**Writes.** Summarises the chosen stories from their real source text. No hallucinated facts, no fabricated links: every URL comes from a real search result.**Delivers.** Emails you a newspaper-style digest, at the time you choose.\n\nIt's a tool-using agent, not a fixed script: it decides what to search, judges what's worth keeping, and searches again if the results are thin.\n\n**In the email:** your N top stories, each with a factual summary, source and publication\ndate · a word of the day · a quote of the day. Optionally, one slot is reserved for a\n**focus theme** you never want to miss.\n\nOne readable file, no code, no secrets:\n\n```\ntitle: Daily Briefing        # shown in the email subject + header\nnum_articles: 3              # how many stories per morning\n\ntopics:                      # what the agent researches (free text)\n  - AI & LLMs (GPT, Claude, agents, MCP)\n  - automation & no-code (n8n, Zapier, Make)\n  - dev tools & open source\n\nfocus:                       # OPTIONAL: a story that's ALWAYS included\n  label: shipping / maritime / logistics\n  priority_query: CMA CGM    # searched first; preferred when there's fresh news\n  keywords: [cma cgm, shipping, maritime, container, freight]\n\nmodel: openai/gpt-oss-120b   # the Groq model the agent thinks with\nmodel_fallbacks:             # tried in order if the one above is retired\n  - openai/gpt-oss-20b\n  - qwen/qwen3.8-27b\n```\n\nTopics are free text, so point it at finance, climate, football, your industry, whatever\nyou want to wake up informed about. Don't want a guaranteed story? Delete the `focus:`\n\nblock. No `config.yaml`\n\nat all? Sensible defaults keep it running.\n\nNothing stays on. No server, no container, no cron on your laptop.\n\n**1.** Click ** Use this template**\nto get your own copy (or fork it). Then clone it and run:\n\n```\npython main.py --setup\n```\n\nThe wizard asks for each key, **verifies it against the real API before saving**, so a wrong\nGmail App Password fails here instead of silently at 8am tomorrow. It clears the inherited\nde-duplication history, offers to upload your secrets with the `gh`\n\nCLI, and prints your\npinger URL with your repo already filled in.\n\n**2.** Add 5 **Settings → Secrets and variables → Actions** secrets (the wizard can do\nthis for you if you have the `gh`\n\nCLI):\n\n| Secret | Where to get it |\n|---|---|\n`GROQ_API_KEY` |\n|\n\n`TAVILY_API_KEY`\n\n[app.tavily.com](https://app.tavily.com), free, 1000 searches/month`GMAIL_ADDRESS`\n\n`GMAIL_APP_PASSWORD`\n\n[16-char App Password](https://myaccount.google.com/apppasswords)(needs 2FA).**Not** your Gmail password`RECIPIENT_EMAIL`\n\n**3.** Test it now: **Actions → Daily Briefing → Run workflow**, tick `force`\n\n.\n\n**4.** Make it punctual by adding a free external pinger (5 minutes, one time):\n\n**Why, and how to set up the free cron pinger**\n\nGitHub's own `schedule:`\n\ncron is unreliable on low-activity repos: it drops most\ntriggers and can fire hours late, so \"before 10am\" isn't guaranteed by it alone. The fix,\nstill 100% free, is a tiny external cron that calls GitHub at a fixed time. GitHub's own\ncron stays on as a best-effort backup.\n\n**a) Create a GitHub token** at [new fine-grained token](https://github.com/settings/personal-access-tokens/new)\n\n- Repository access:\n*Only select repositories*→ this repo - Permissions →\n**Actions: Read and write** - Copy the\n`github_pat_...`\n\nvalue\n\n**b) Create a free job on cron-job.org**, daily at ~08:00 in your\ntimezone:\n\n**Method**:`POST`\n\n**URL**:`https://api.github.com/repos/<you>/daily-briefing/actions/workflows/daily-briefing.yml/dispatches`\n\n**Body**:`{\"ref\":\"main\"}`\n\n**Headers**:`Accept: application/vnd.github+json`\n\n`Authorization: Bearer <your github_pat_...>`\n\n`X-GitHub-Api-Version: 2022-11-28`\n\nA successful call returns **HTTP 204**. The pinger fires *without* forcing, so it still\npasses the morning-window and once-a-day guards, so it can never double-send.\n\nRenamed the repo later? Update the URL by hand; GitHub redirects GETs but not reliably POSTs.\n\nThe things this does differently, and what they cost:\n\nNo paid step, anywhere |\nIt runs inside the Groq, Tavily and GitHub Actions free tiers. A daily run uses ~180 of Tavily's 1000 monthly searches. Worth being clear: those are three companies' free tiers, not a guarantee I can make. If one changes, this changes. |\nNo infrastructure |\nNo server, no Docker, no database, no always-on process. The trade-off is that you're running on GitHub's schedule and inside their limits. |\nAn agent, not an RSS pipeline |\nIt picks its own search queries, judges result quality, and searches again when results are thin, which also means it's slower and less predictable than a feed reader. |\nNever repeats a story |\nSent articles are matched by normalised URL and headline, kept in `data/history.json` and committed back to the repo. No database to run; the trade-off is a daily commit in your history. |\nNever double-sends |\nA morning-window guard plus a once-a-day guard, so several triggers still produce one email. |\nSurvives model deprecations |\nThe model is configurable with a fallback chain. This exists because a hardcoded model was retired by the provider and took the agent down for a day. |\nGrounded summaries |\nWritten only from real source text, and any URL the model didn't actually find in a search result is dropped before it can reach the email. |\nKeys stay yours |\nThey live in your own repo's secrets or your local `.env` . The agent talks to Groq, Tavily and Gmail's SMTP server, and nothing else. |\n\n**What does it cost?**\n\nNothing. A run uses a handful of Groq calls and up to 6 Tavily searches, well inside both free tiers (Tavily gives 1000 searches/month; the briefing uses ~180). GitHub Actions is free for public repos. cron-job.org is free. There is no paid tier to graduate into.\n\n**Where do my API keys live? Is anything sent to you?**\n\nYour keys live in **your** GitHub repo's Actions secrets (or your local `.env`\n\n, which is\ngitignored). The agent talks only to Groq, Tavily and Gmail's SMTP server. Nothing is\nsent anywhere else, and the author never sees your keys, topics or briefings.\n\n**How do I change the model?**\n\nEdit `model:`\n\nin `config.yaml`\n\nto any model your Groq key can reach, and list backups\nunder `model_fallbacks:`\n\n. You can also override it without editing the file by setting\nthe `GROQ_MODEL`\n\nenvironment variable (or a `GROQ_MODEL`\n\nGitHub Actions *variable*).\nProviders retire models regularly, so check\n[Groq's deprecations page](https://console.groq.com/docs/deprecations) for what's live.\n\n**Can I use something other than Gmail?**\n\nYes, with a small edit. `email_sender.py`\n\nuses plain `smtplib`\n\nover SSL, so pointing\n`SMTP_HOST`\n\n/ `SMTP_PORT`\n\nat another provider (Fastmail, Zoho, a company relay) and using\nthat account's credentials works. Gmail is the default only because its App Passwords are\nfree and easy.\n\n**Can I change the delivery time?**\n\nYes. The time is set by your cron-job.org job; the workflow only enforces a\n**06:00–11:00 Europe/Paris** safety window so a stray trigger can't email you at night.\nTo move outside that window, edit the hour list in the gate step of\n`.github/workflows/daily-briefing.yml`\n\n(and `TIMEZONE`\n\nin `scheduler.py`\n\nfor local runs).\n\n**What if it fails one morning?**\n\nIt retries once after two minutes. If that also fails you get a short \"couldn't send today\" email explaining why, the full traceback goes to the Actions log, and the run is marked failed, so a broken morning is red in the Actions tab rather than a green check. The next morning's run is unaffected.\n\n**Can I get more than 3 stories, or several briefings a day?**\n\nSet `num_articles:`\n\nto whatever you like. One briefing per day is enforced by design,\nbecause the de-duplication history keys on the date, so a second daily send would need that\nguard changed.\n\n```\npython main.py --setup    # guided setup (or copy .env.example by hand)\npython main.py --dry-run  # preview only, sends nothing\npython main.py --now      # build & send one briefing now\npython main.py            # run the scheduler (daily at 09:00 Europe/Paris)\npip install -r requirements-dev.txt\npytest -q\n```\n\nMocked network, no API keys needed, runs in under a second. Covers the model fallback chain, config defaults, de-duplication and both send guards. CI runs it on every push.\n\n| Tool | Role |\n|---|---|\n| Python 3.11+ | Core language. 5 dependencies, no framework |\nGroq (`openai/gpt-oss-120b` ) |\nThe agent's brain. Configurable, with fallbacks |\n| Tavily | Real-time news search |\n| GitHub Actions | Runtime for the daily job |\n| cron-job.org | External pinger, so it fires on time |\n\n```\ndaily-briefing/\n├── config.yaml            # your preferences: title, topics, focus, model\n├── config.py              # loads/validates config.yaml, checks required secrets\n├── main.py                # entry point (--setup / --dry-run / --now)\n├── setup_wizard.py        # guided setup, verifies every key\n├── agent.py               # the agent: research loop, writing, HTML rendering\n├── email_sender.py        # Gmail delivery\n├── scheduler.py           # the briefing jobs + local daily scheduler\n├── history.py             # remembers sent articles (no repeats)\n├── data/history.json      # the de-duplication memory\n├── docs/                  # screenshot + sample briefing\n├── tests/                 # pytest suite (no network)\n└── .github/workflows/     # daily-briefing.yml (the morning run) + ci.yml\n```\n\nIssues and PRs welcome. See [CONTRIBUTING.md](/tballochi/daily-briefing/blob/main/CONTRIBUTING.md).\n\nMIT. See [LICENSE](/tballochi/daily-briefing/blob/main/LICENSE).\n\nBuilt by [Timoté Ballochi](https://github.com/tballochi).", "url": "https://wpnews.pro/news/a-free-ai-news-briefing-agent-that-runs-on-github-actions-no-server", "canonical_source": "https://github.com/tballochi/daily-briefing", "published_at": "2026-08-29 08:02:43+00:00", "updated_at": "2026-08-29 08:19:05.913206+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-products"], "entities": ["GitHub Actions", "Groq", "Tavily", "tballochi/daily-briefing"], "alternates": {"html": "https://wpnews.pro/news/a-free-ai-news-briefing-agent-that-runs-on-github-actions-no-server", "markdown": "https://wpnews.pro/news/a-free-ai-news-briefing-agent-that-runs-on-github-actions-no-server.md", "text": "https://wpnews.pro/news/a-free-ai-news-briefing-agent-that-runs-on-github-actions-no-server.txt", "jsonld": "https://wpnews.pro/news/a-free-ai-news-briefing-agent-that-runs-on-github-actions-no-server.jsonld"}}