Skip to content

ford-graham/hantanet

Repository files navigation

HantaNet

HantaNet is an ElizaOS agent that watches for hantavirus-related signals, generates public-health style risk analysis, posts recurring global heatmap updates, and replies to Twitter/X mentions with location-specific hantavirus forecasts.

The project combines an OpenAI-backed character, the ElizaOS Twitter runtime, a custom hantavirus plugin, and an Obsidian Markdown persistence plugin for durable surveillance notes.

What It Does

  • Runs a HantaNet agent persona focused only on hantavirus, rodent reservoirs, outbreak surveillance, and DeSci disease monitoring.
  • Periodically synthesizes hantavirus research context with OpenAI.
  • Generates region-level risk data for a global hantavirus heatmap.
  • Creates a heatmap image with OpenAI image generation.
  • Uploads the generated image to Twitter/X and publishes a scheduled risk-map tweet.
  • Watches Twitter/X mentions, detects locations, and replies with short location-specific risk forecasts.
  • Feeds detected mention locations back into the next heatmap as live public-interest signals.
  • Accepts user-submitted report: field reports as unverified ecological signals.
  • Publishes a compact DeSci signal leaderboard in heatmap posts.
  • Stores mention signals and field reports in an Obsidian-compatible Markdown vault.
  • Keeps replies under tweet length and deduplicates mention responses through the Eliza runtime cache.

Core Pipeline

ElizaOS runtime
  |
  | loads src/index.ts project
  v
HantaNet character + plugins
  |
  | built-in plugins
  | - SQL storage
  | - OpenAI model provider
  | - Bootstrap runtime
  | - Twitter/X integration
  | - Obsidian Markdown persistence
  |
  | custom plugin
  v
src/plugins/hantavirus
  |
  | research provider
  | - refreshes research context every 5 minutes
  | - injects recent context into agent generations
  |
  | heatmap cron
  | - generates region risk data
  | - creates image with OpenAI image API
  | - uploads media to Twitter/X
  | - publishes scheduled heatmap post
  |
  | mention diagnostics
  | - searches for @HantaNet mentions
  | - filters out self-authored tweets
  | - detects locations with lists and GPT fallback
  | - records mention locations for the next heatmap loop
  | - parses "report:" submissions as unverified field reports
  | - generates a location-specific risk reply
  | - caches replied tweet IDs
  |
  | surveillance signals
  | - writes mention-to-map location signals to Obsidian daily notes
  | - writes unverified user field reports to Obsidian daily notes
  | - generates DeSci signal leaderboard context for map tweets

Project Structure

characters/hantanet.json
  HantaNet persona, voice, knowledge, topics, and response style.

src/index.ts
  ElizaOS project export. Registers SQL, OpenAI, Bootstrap, Twitter, Obsidian persistence, and the custom hantavirus plugin.

src/character.ts
  Loads the HantaNet character JSON for the runtime.

src/plugins/hantavirus/index.ts
  Custom plugin registration. Wires actions, providers, and startup hooks.

src/plugins/hantavirus/providers/researchProvider.ts
  Injects fresh hantavirus research context into the agent.

src/plugins/hantavirus/services/researchService.ts
  Maintains the in-memory research cache and generates research/risk summaries with OpenAI.

src/plugins/hantavirus/services/imageGeneration.ts
  Generates the global heatmap image from region risk data.

src/plugins/hantavirus/actions/heatmapTweet.ts
  Runs the scheduled heatmap pipeline and posts the image tweet.

src/plugins/hantavirus/actions/mentionResponder.ts
  Detects locations in mentions and generates location-specific reply forecasts.

src/plugins/hantavirus/services/twitterMentionDiagnostics.ts
  Polls Twitter/X mentions, logs diagnostics, deduplicates replies, and sends direct replies.

src/plugins/hantavirus/services/surveillanceSignals.ts
  Routes mention-to-map signals, user-submitted field reports, and leaderboard summaries through Obsidian persistence, with runtime cache fallback.

src/plugins/obsidianPersistence/index.ts
  ElizaOS plugin and service for writing HantaNet surveillance entries into Obsidian-compatible Markdown daily notes.

ecosystem.config.cjs
  PM2 process definition for running the ElizaOS agent on a VM.

Custom Plugin

The custom plugin lives in src/plugins/hantavirus and is registered as:

export const hantavirusPlugin: Plugin = {
  name: "hantavirus",
  actions: [heatmapTweetAction, mentionResponderAction],
  providers: [researchProvider],
  init: async (_config, runtime) => {
    void startHeatmapCron(runtime);
    startTwitterMentionDiagnostics(runtime);
  },
};

The project registers obsidianPersistencePlugin before hantavirusPlugin so surveillance data is written to Markdown as the durable source of truth.

Obsidian Persistence

By default, surveillance notes are stored in:

obsidian-vault/HantaNet/Surveillance/YYYY-MM-DD.md

Each daily note contains human-readable entries plus machine-readable JSON blocks for mention signals and field reports. The default obsidian-vault/ directory is ignored by git because it contains live operational data.

Set a different vault path with:

OBSIDIAN_VAULT_PATH=./obsidian-vault

If the Obsidian service is unavailable, HantaNet logs a warning and falls back to runtime cache.

Research Provider

researchProvider refreshes synthesized hantavirus research every five minutes and injects the latest context into the agent. The prompt biases generation toward concrete mechanisms like reservoir movement, seasonal exposure, rodent ecology, surveillance lag, and DeSci monitoring.

Heatmap Tweet Action

The heatmap pipeline:

  1. Generates current region-level risk data with OpenAI.
  2. Pulls mention-to-map hotspots, unverified field reports, and the DeSci signal leaderboard from Obsidian daily notes.
  3. Sends research context plus live signal context into the risk-data prompt.
  4. Sends that risk data into the image-generation prompt.
  5. Uploads the image through Twitter/X OAuth 1.0a media upload.
  6. Posts a tweet with the uploaded media ID and compact DeSci signal leaderboard.
  7. Repeats on HEATMAP_INTERVAL_MINUTES.

Mention-To-Map Loop

Every mention with a detected location is stored as a public-interest signal. The next heatmap generation includes the highest-frequency mention locations so repeated questions about a region can slightly raise map attention without being treated as confirmed case data.

User Field Reports

Users can submit unverified ecological observations with:

@HantaNet report: rodent droppings in a shed near Boulder, Colorado

HantaNet records the report in the current Obsidian daily note, replies with an acknowledgement, and feeds the signal into the next heatmap and leaderboard. Field reports are explicitly labeled as unverified and must not be interpreted as confirmed cases.

DeSci Signal Leaderboard

The leaderboard ranks locations by live mentions and field reports. Field reports receive extra weight because they describe direct environmental observations, but the generated copy labels them as unverified ecological signals.

Mention Responder

The mention pipeline:

  1. Reads incoming mention text.
  2. Looks for known US states, state abbreviations, countries, and major cities.
  3. Falls back to OpenAI location extraction if no quick match is found.
  4. Generates a short risk forecast for that location.
  5. Replies through the Twitter plugin callback or diagnostic service.

Mention Diagnostics

twitterMentionDiagnostics polls for @username mentions, prints useful runtime logs, filters out the bot's own tweets, and stores hantanet/twitter/mention-replied/<tweetId> cache keys so the same tweet is not answered repeatedly.

Environment

Create a .env from the template:

cp .env.example .env

Required values:

OPENAI_API_KEY=
OPENAI_LARGE_MODEL=gpt-5.5
OPENAI_SMALL_MODEL=gpt-5.5

TWITTER_API_KEY=
TWITTER_API_SECRET_KEY=
TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_TOKEN_SECRET=

Runtime controls:

TWITTER_ENABLE_POST=true
TWITTER_POST_INTERVAL_MIN=15
TWITTER_POST_INTERVAL_MAX=30
TWITTER_SEARCH_ENABLE=true
TWITTER_AUTO_RESPOND_MENTIONS=true
TWITTER_POLL_INTERVAL=30
TWITTER_DRY_RUN=false

HEATMAP_INTERVAL_MINUTES=60
HANTANET_MENTION_DIAGNOSTIC_INTERVAL_MS=60000
OBSIDIAN_VAULT_PATH=./obsidian-vault

Never commit .env. The repository intentionally tracks .env.example only.

Local Development

Install dependencies:

npm install

Build TypeScript:

npm run build

Run the Obsidian persistence checks:

npm run test:obsidian

Run through ElizaOS:

./node_modules/.bin/elizaos start

If the CLI was installed with a Bun shebang, this also works:

/home/ubuntu/.bun/bin/bun ./node_modules/.bin/elizaos start

VM Deployment With PM2

The production process should run the ElizaOS CLI, not node dist/index.js. The compiled dist/index.js exports the ElizaOS project; the ElizaOS runtime is what actually starts the agent.

Recommended PM2 config:

module.exports = {
  apps: [
    {
      name: "hantanet",
      cwd: "/home/ubuntu/hantanet",
      script: "/home/ubuntu/.bun/bin/bun",
      args: "./node_modules/.bin/elizaos start",
      interpreter: "none",
      exec_mode: "fork",
      instances: 1,
      autorestart: true,
      max_memory_restart: "500M",
      env: {
        NODE_ENV: "production",
        PATH: "/home/ubuntu/.bun/bin:/usr/local/bin:/usr/bin:/bin",
      },
      error_file: "logs/error.log",
      out_file: "logs/out.log",
      log_date_format: "YYYY-MM-DD HH:mm:ss",
    },
  ],
};

Start or restart:

mkdir -p logs
pm2 delete hantanet
pm2 start ecosystem.config.cjs --update-env
pm2 logs hantanet --lines 100

Persist across reboot:

pm2 save
pm2 startup

Run the command printed by pm2 startup, then:

pm2 save

Expected Logs

When the runtime is wired correctly, logs should include messages like:

[HantaNet:Research] Refreshing hantavirus research data...
[HantaNet:Heatmap] Starting heatmap cron...
[HantaNet:Heatmap] Generating hourly heatmap...
[HantaNet:MentionDiag] Checking Twitter mentions...

If PM2 shows the process as online but logs stay empty, confirm PM2 is running the ElizaOS CLI through Bun instead of running dist/index.js directly.

Operational Notes

  • Use Node 22 on the VM.
  • Use Bun when running the ElizaOS CLI if the installed binary has a Bun shebang.
  • Keep .env off GitHub.
  • Rotate API keys immediately if they are pasted into logs, chats, screenshots, or issue trackers.
  • Use TWITTER_DRY_RUN=true while testing credentials and startup behavior.
  • Watch pm2 logs hantanet after every deploy to confirm the Twitter client becomes ready before mention polling starts.

Commands

npm run build
pm2 start ecosystem.config.cjs --update-env
pm2 restart hantanet --update-env
pm2 logs hantanet --lines 100
pm2 status hantanet
pm2 save

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors