$OBELIS◆CA pending◆Solana◆Every coin has a legend◆Every chapter hashed into a Solana memo◆$OBELIS◆CA pending◆Solana◆Every coin has a legend◆Every chapter hashed into a Solana memo◆$OBELIS◆CA pending◆Solana◆Every coin has a legend◆Every chapter hashed into a Solana memo◆$OBELIS◆CA pending◆Solana◆Every coin has a legend◆Every chapter hashed into a Solana memo◆

Docs / Build on it

Run your own

Every setting, Privy, Claude, the memo signer, X and Telegram keys, Docker and checks.

What you need

What forRequired
A serverLinux with Node.js 22.13 or newer, or Docker. 1 CPU and 1 GB of memory run it; building the site wants about 2 GB.yes
A domain with HTTPSThe site, link previews on X and Telegram, Privy.yes
A Solana RPCEvery fact is read through it. It must answer getProgramAccounts and transaction history (e.g. Helius, Triton, QuickNode).yes
A Claude API keyThe chronicler. From console.anthropic.com.yes
A little SOLFor the memo signer: about 0.000005 SOL per chapter; 0.05 SOL lasts thousands of chapters.yes, to carve
A Privy App IDSign-in with email or X, with an embedded Solana wallet.no
X API keysThe agent posts each chapter on your X account.no
A Telegram bot and channelThe agent posts each chapter in your channel.no
Your token’s addressShown as the only official CA; holder perks.no

Install

  1. 1

    Get the code and install

    shell
    cd /opt/obelis
    npm ci
  2. 2

    Write the settings

    shell
    npm run setup
    It asks for the domain, the Claude key, the model, the RPC, Privy and the token, makes APP_SECRET and a fresh memo signer wallet, and writes .env.local (an older one is kept as .env.local.bak). Or copy .env.example to .env.local and fill it in.
  3. 3

    Check everything

    shell
    npm run doctor
  4. 4

    Build

    shell
    npm run build
  5. 5

    Run the site and the worker

    Two processes: the site (npm start, port 3000) and the agent (npm run worker). Run exactly one worker. Ready-made units are in deploy/: systemd (obelis-web.service, obelis-worker.service), pm2 (ecosystem.config.cjs) and nginx with HTTPS (nginx.conf), or use Docker.

Every setting

All settings live in .env.local on the server. Only NEXT_PUBLIC_* values reach the browser, and they are built into the site: change one, then build again. The others are read when the processes start: change one, then restart them.

The site and its links

SettingWhat it doesRead by
NEXT_PUBLIC_SITE_URLhttps://obelis.exampleThe public address of the site, with https. Used in links, previews, feeds and the sign-in message.site + browser
APP_SECRETSigns sign-in sessions. A long random string (openssl rand -hex 32). Without it sessions reset on every restart.site
NEXT_PUBLIC_X_URLhttps://x.com/your_handleYour X account: shown in the header, footer and on the agent page; share links mention it.site + browser
NEXT_PUBLIC_X_HANDLEyour_handleThe handle alone also works (without @).site + browser
NEXT_PUBLIC_TELEGRAM_URLhttps://t.me/your_channelYour Telegram channel’s public link.site + browser
NEXT_PUBLIC_SOURCE_URLWhere your code is published, if it is. Shown on the agent page.site + browser

Sign-in

SettingWhat it doesRead by
NEXT_PUBLIC_PRIVY_APP_IDPrivy App ID. Empty: Solana wallets only. See Privy.site + browser

Solana

SettingWhat it doesRead by
SOLANA_RPC_URLhttps://mainnet.helius-rpc.com/?api-key=…The RPC every fact is read and every memo is sent through.both
HISTORY_LIMITTransactions read per reading, at most (500).worker

The chronicler

SettingWhat it doesRead by
ANTHROPIC_API_KEYsk-ant-…The Claude API key. Only the worker reads it; it is never logged or sent to the browser.worker
AGENT_MODELclaude-opus-5-5 (default, the best writing), claude-sonnet-5-5 (about half the price) or claude-haiku-5-5 (the cheapest).worker
AGENT_EFFORTlow, medium (default), high, xhigh, max, or default for the model’s own.worker
AGENT_FALLBACKSdefault: when a safety classifier declines, the API may retry on another Claude model. off: no fallback.worker
AGENT_MAX_TURNSDrafts per chapter, at most (5).worker
AGENT_MAX_TOKENSRoom for thinking plus the chapter (16000).worker
AGENT_CHAPTER_MAX_USDSpend per chapter, at most ($0.30).worker
AGENT_DAILY_USDSpend per UTC day for the whole site ($20).both
AGENT_PRICES{"claude-opus-5-5":{"in":4,"out":20}}Override the price table, as JSON, if your prices differ.worker
AGENT_DRY_RUN1: a scripted test chronicler instead of Claude, no key, no cost, clearly labelled on the site. For testing only.worker
ANTHROPIC_BASE_URLOnly if you reach the Claude API through a gateway.worker

Chronicles

SettingWhat it doesRead by
CHAPTER_EVERY_HOURSHow often each chronicle is read (24).both
BIG_BUY_SOLA buy of at least this many SOL is a large buy (5).both
CHRONICLE_FORwallets (default), holders or creators: who may begin a chronicle.site
CHRONICLES_PER_WALLET_PER_DAYChronicles one wallet may begin a day (3).site
MAX_CHRONICLESChronicles being read, at most (500).site

Carving

SettingWhat it doesRead by
MEMO_SIGNER_KEYThe memo signer’s secret key: base58, or the JSON array solana-keygen writes. Empty: no carving.worker
MEMO_SIGNER_ADDRESSIts public address, so the site can check memo signers even while the worker is offline.site
MEMO_LOW_SOLBelow this balance the site and /api/health warn (0.01).both
MEMO_PROGRAMMemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHrThe SPL Memo program (leave as it is).both

Posting chapters

SettingWhat it doesRead by
X_API_KEY, X_API_SECRETYour X app’s API key and secret (OAuth 1.0a consumer keys). See X.worker
X_ACCESS_TOKEN, X_ACCESS_SECRETThe access token and secret of the account that posts, made after the app has Read and Write permission.worker
X_POST_CHAPTERS0 turns X posting off without removing the keys.worker
TELEGRAM_BOT_TOKEN123456:ABC…The bot from @BotFather (also used by the optional CA bot below).both
TELEGRAM_CHANNEL_ID@your_channelThe channel: @channelname, or the -100… id of a private one.worker
TELEGRAM_POST_CHAPTERS0 turns Telegram posting off.worker
ANNOUNCE_PER_DAYPosts per UTC day on each, at most (15).both

Token

SettingWhat it doesRead by
TOKEN_CAYour token’s mint: the only official address the site shows. Empty: “CA pending”.site
TOKEN_SYMBOLIts ticker (OBELIS).site
HOLDER_MIN_TOKENSHow many tokens make a holder (with CHRONICLE_FOR=holders).site
SWAP_WIDGET1 shows Jupiter’s swap, locked to TOKEN_CA; 0 hides it.site
TOKEN_MARKET_DATA0 turns off the price and volume from DexScreener.site
SWAP_CSP_CONNECTExtra hosts the swap widget may connect to, if Jupiter needs new ones.site

The optional CA bot

SettingWhat it doesRead by
TELEGRAM_ADMIN_IDYour Telegram user id: while TOKEN_CA is empty, you can send the CA to the bot and the site shows it.site
TELEGRAM_WEBHOOK_SECRETA random string; scripts/set-webhook.mjs registers the webhook with it.site

Storage

SettingWhat it doesRead by
OBELIS_DATAWhere the database, the coin images and the CA file live (./data). Back it up.both

Privy

  1. 1

    Create an app

    At dashboard.privy.io ↗, create an app. Its App ID is in the app’s settings; put it in NEXT_PUBLIC_PRIVY_APP_ID.
  2. 2

    Allow your domain

    In Configuration → App settings → Domains, add your site’s origin exactly as in NEXT_PUBLIC_SITE_URL (https://…). Add http://localhost:3000 too if you test locally.
  3. 3

    Turn on the login methods

    In the login methods, turn on Email, X (under socials) and External wallets with Solana. The site asks for exactly these three; one that is off in the dashboard shows “This login option is turned off”.
  4. 4

    Solana wallets

    Make sure Solana is on for embedded wallets. The site asks Privy to create one for users who log in with email or X and have no wallet.
  5. 5

    Build again

    The App ID is built into the site: npm run build, then restart. npm run doctor checks that it looks right.
The site’s Content-Security-Policy already allows Privy’s hosts (auth.privy.io, its wallet frames and WalletConnect). Privy never gives the site a key: users sign the same sign-in message as with any wallet. If Privy does not answer in a visitor’s browser within a few seconds (an ad blocker, a network, or your domain missing in step 2), Connect offers the browser’s own Solana wallets instead. npm run privy:check tests that against a build with your App ID.

Claude

  1. 1

    Get a key

    At console.anthropic.com ↗ → Settings → API keys. Put it in ANTHROPIC_API_KEY on the server.
  2. 2

    Add credit and set a limit

    Add credit in Billing, and set a monthly spend limit there too; AGENT_DAILY_USD is the site’s own limit on top of it.
  3. 3

    Try one chapter

    npm run agent:test writes one test chapter with your key and settings, and shows its cost. npm run doctor -- --live does it as part of the checks.

What it costs depends on the model, the effort and how much happens: a quiet day costs nothing; a chapter is usually a few cents on Opus, less on Sonnet, a fraction of a cent on Haiku. The agent page shows the real average per chapter once it runs.

The memo signer

  • —npm run setup makes a new wallet for it and writes both MEMO_SIGNER_KEY and MEMO_SIGNER_ADDRESS. Use a wallet that does nothing else.
  • —Send it a little SOL (0.05 is plenty). The agent page and /api/health show its balance and warn below MEMO_LOW_SOL.
  • —It only ever signs memos. If its key leaked, the worst anyone could do is write memos in its name: make a new one, and the agent page shows the new address.

X

  1. 1

    A developer account

    At developer.x.com ↗, sign in with the account that will post, and create a project and an app.
  2. 2

    Read and Write

    In the app’s User authentication settings, set the app permissions to Read and Write (the type can be Web App; the callback and website can be your site’s address).
  3. 3

    Keys and tokens

    In Keys and tokens: the API Key and Secret go in X_API_KEY and X_API_SECRET. Then generate the Access Token and Secret (after setting Read and Write, or they will be read-only) and put them in X_ACCESS_TOKEN and X_ACCESS_SECRET.
  4. 4

    Restart the worker

    The worker log says “posts to x”. npm run doctor -- --live signs in to X with the keys and names the account that will post.

Posting limits

X API plans limit how many posts an app may make. Keep ANNOUNCE_PER_DAY under your plan’s daily allowance; past it, chapters are still on the site and in RSS.

Telegram

  1. 1

    A bot

    In Telegram, ask @BotFather ↗ for a new bot (/newbot). Its token goes in TELEGRAM_BOT_TOKEN.
  2. 2

    A channel

    Create a public channel (or use yours). Add the bot as an administrator with the right to post messages.
  3. 3

    Tell the worker

    TELEGRAM_CHANNEL_ID=@your_channel (a private channel: its -100… id). And NEXT_PUBLIC_TELEGRAM_URL=https://t.me/your_channel so the site links it.
  4. 4

    Restart

    The worker log says “posts to telegram”. npm run doctor -- --live asks Telegram whether the bot may post in the channel.

Docker

shell
npm run setup                       # or write .env.local by hand
docker compose up -d --build        # the site on 127.0.0.1:3000 and the worker
docker compose exec web npm run doctor
docker compose logs -f worker

The site and the worker share one volume (/data) with the database and the coin images. The image is built with .env.local as a build secret (the NEXT_PUBLIC_* values are built in); it is not copied into the image. Put nginx or Caddy with HTTPS in front (deploy/nginx.conf).

Checks

CommandWhat it checks
npm run doctorEvery setting, the keys’ format, the RPC (and that it answers what the facts need), the memo signer and its balance, the build, the worker, the site, X and Telegram settings.
npm run doctor -- --liveAlso: one real chapter on Claude (a few cents), the X sign-in, the Telegram channel.
npm run agent:testOne test chapter with your Claude settings, with its cost.
npm testThe unit tests, with pump.fun’s real programs on a local chain.
GET /api/healthFor an uptime monitor: ok, the worker, and problems to fix.

Operating

npm run admin -- …What it does
statusChronicles due, today’s spend, the worker, the memo signer.
pause / resumeStop or restart all reading (the site stays up).
check <mint>Read this chronicle now.
hide <mint> / show <mint>Take a chronicle off the site, or bring it back. Its memos stay on chain.
hide-chapter <id> / show-chapter <id>The same for one chapter.
stop <mint> / start <mint>Stop or restart reading one chronicle; its chapters stay.
ban <wallet> [reason] / unban <wallet>Block a wallet from beginning chronicles.
  • —Back up OBELIS_DATA: the SQLite database (copy it with the site and worker stopped, or live with sqlite3 data/obelis.db ".backup obelis-backup.db") and the coin images next to it.
  • —To update: get the new code, npm ci, npm run build, restart both. The database upgrades itself on start.
  • —Keep .env.local readable by the service user only (chmod 600). Never put a secret in a NEXT_PUBLIC_ setting.

Not investment advice. Obelis tells what the chain shows about a coin, as a story. Begin a chronicle.

Run your own · Docs · Obelis