cd /news/ai-agents/i-run-my-agency-s-10-ai-agents-from-… · home topics ai-agents article
[ARTICLE · art-54950] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

I run my agency's 10 AI agents from launchd, no n8n, 0 dependencies

Digiton Dynamics runs 10 production AI agents using a plain Node.js framework with zero dependencies, replacing complex orchestration tools like n8n. Each agent is a single file triggered by launchd, and every irreversible action passes through a unit-tested guardrail system that enforces kill switches, allowlists, do-not-contact lists, deduplication, and daily caps to prevent errors. The open-source template is designed to be safe by default, requiring explicit opt-in before any messages can be sent.

read4 min views1 publishedJul 10, 2026
I run my agency's 10 AI agents from launchd, no n8n, 0 dependencies
Image: source

Ten scheduled AI agents run my agency's operations: lead research, PR outreach, reply triage, pipeline sync, inbox monitoring, daily briefings. No n8n, no framework, no queue, no Docker. Each agent is one Node file on a launchd timer, and every irreversible action passes a unit-tested guardrail before it happens. This repo is that harness, extracted clean as a template.

The numbers:

  • 10 agents in production at Digiton Dynamics - 0 runtime dependencies, no build step, plain Node
  • 27 unit tests on the rails, CI on Node 18/20/22
  • 1 file per agent
  • 5 rails on every action: kill switch, do-not-contact, allowlist, dedupe, daily cap
$ npm run demo

[INFO] DRAFT ana@oceanview-realty.pt :: allowed, not sent (mode=draft)
[INFO] BLOCK carlos@montebianco.com :: recipient not on allowlist
[INFO] BLOCK info@donotcontact.example :: recipient on DNC
[INFO] done proposed=3 allowed=1 blocked=2 performed=0 killed=false

Why the rails exist: we once ran an unguarded email bot on a timer in production. No allowlist, no daily cap, no off switch - one bad prompt or one bad data row away from mailing the wrong list at 3am. We shut it down and rebuilt every scheduled agent behind a single pure function, evaluate()

in lib/rails.js

. Nothing irreversible happens without clearing it.

Safe by default on a fresh clone: empty allowlist, stub sender, dry-run unless you opt in.

proposed action  ->  [ rails ]  ->  sent  or  blocked (with a reason)
git clone https://github.com/Botfather90/digiton-agent-fleet
cd digiton-agent-fleet

npm test          # run the rails unit tests (zero setup)
npm run demo      # watch the rails decide on sample leads (no credentials)

Prefer your own copy: click Use this template at the top of the GitHub page to start a fresh repo from this one.

The demo runs the example follow-up agent in dry-run against three sample leads and shows the rails at work: one allowed, one blocked for not being on the allowlist, one blocked by the do-not-contact list. A second worked example, jobs/example-digest.js

, posts a daily digest to a Slack-style webhook channel, a non-email action routed through the same evaluate()

so recipient, DNC, allowlist, dedupe, and daily cap all still apply. Run it with npm run demo:digest

.

Every action the agent proposes is checked by evaluate()

in lib/rails.js

, in this order, hardest stop first:

Check Behavior
Kill switch touch state/KILL freezes every job instantly. Nothing runs until you rm it.
Recipient Must be present, or the action is dropped.
DNC Do-not-contact list (email or domain). Always wins, even over the allowlist.
Allowlist An empty allowlist means nothing sends. You opt in addresses or whole domains.
Dedupe A dedupeKey is never actioned twice within the retention window (30 days). History is pruned by age, not kept forever.
Daily cap Stop after N actions per day. The counter resets at midnight UTC.

evaluate()

is a pure function with no I/O, so it is fast to reason about and fully unit-tested.

The template cannot email anyone on a fresh clone. The example sender is a deliberate stub, the default allowlist is empty, and the runner is dry-run unless you pass --send

and set "mode": "send"

. You have to opt in to every one of those before a single message leaves.

A job is one file in jobs/

that exports two functions:

// jobs/my-agent.js
module.exports = {
  // Return the actions you want to take. The runner does NOT trust these,
  // it runs each one through the rails before anything happens.
  async propose({ config, log }) {
    return [
      { to: 'lead@example.com', subject: 'Hello', body: '...', dedupeKey: 'lead-2026-06' },
    ];
  },

  // Perform ONE allowed action. This is the only place that actually sends.
  async send(action, { config, log }) {
    // your real sender goes here: Gmail, Resend, SES, an API call, whatever
  },
};

Run it: node bin/run-job.js my-agent

(dry-run) or add --send

to dispatch allowed actions.

Set your policy in fleet.config.json

(copy fleet.config.example.json

):

{
  "mode": "draft",
  "policy": {
    "dailyCap": 30,
    "allowEmails": ["vip@client.com"],
    "allowDomains": ["client.com"],
    "dnc": ["unsubscribed@x.com", "competitor.com"]
  }
}

Your real fleet.config.json

is gitignored, so your allowlist never ends up in a commit.

Pick your platform. All three run the agent daily at 09:00 by default.

  • macOS (launchd): bash schedule/install.sh

installs a launchd job and prints how to run, inspect, and remove it. - Linux (systemd): copy the unit and timer from schedule/systemd/

andsystemctl enable --now agent-fleet.timer

. - Anything with cron: paste the line from schedule/crontab.example

intocrontab -e

.

lib/rails.js                 the guardrail (pure evaluate() + ledger persistence)
lib/log.js                   dated file + stdout logger
bin/run-job.js               the governed runner
jobs/example-followup.js     a worked example agent (stub sender, safe)
jobs/example-digest.js       a daily digest to a Slack-style webhook channel (a non-email action), through the same rails
data/leads.sample.json       sample data so the demo runs with no setup
data/digest.sample.json      sample data for the digest example
test/rails.test.js           unit tests for the rails and the cadence
test/example-digest.test.js  unit tests for the digest job on the same rails
schedule/                    launchd, cron, and systemd templates + installer
.github/workflows/test.yml   node:test on push/PR (Node 18/20/22)

MIT. Built and maintained by Digiton Dynamics.

── more in #ai-agents 4 stories · sorted by recency
── more on @digiton dynamics 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-run-my-agency-s-10…] indexed:0 read:4min 2026-07-10 ·