dexterm

docs › Login with X

Login with X

Read X posts in the SOCIAL panel and on the trending board with your own X app and your own login; what to set up, what the free and paid API tiers allow, and what dexterm never does.

dexterm can read X (Twitter) posts about the open coin and from the people you follow as KOLs. It does this only through your own X developer app and your own X login (OAuth 2.0 with PKCE, user context). There is no shared key, no dexterm account in the loop, no scraping, and dexterm never posts, likes, follows or reads DMs. Every tweet passes the same prompt firewall as every other untrusted text before it is stored, shown or summarised.

Without a login nothing breaks: the trending board runs on the keyless venues, the SOCIAL panel shows on-chain rows and news, and the header says x-stream ○ logged out.

What you set up, once#

  1. An X developer account at developer.x.com (log in with the X account you want to read as).
  2. A Project with an App of type Native App (a public client: PKCE only, no client secret ever exists, so nothing secret ships with dexterm).
  3. In the app's User authentication settings: OAuth 2.0 on, type Native App, callback URL exactly http://127.0.0.1:41287/callback (X requires 127.0.0.1, not localhost, and an exact match), any website URL, and a name/description for the consent screen.
  4. Copy the app's Client ID and store it (prompted on your terminal; never on the command line):
$ dexterm x set-client-id       # paste the Client ID when prompted
$ dexterm x login               # opens your browser on x.com; approve; the tab says "linked"
$ dexterm x status              # @handle, scopes, token age, file modes
$ dexterm x logout              # revokes both tokens at X and deletes the local record

dexterm doctor has a Login with X line that tells you which of these is missing.

Scopes requested: tweet.read users.read list.read offline.access. Nothing with write or dm is ever requested, and the app refuses to build an authorize URL with such a scope.

Where the tokens live#

~/.dexterm/x/tokens.json (mode 600, directory 700; DEXTERM_HOME moves it) next to client-id. The access token lasts two hours and is refreshed with the refresh token when needed; X may rotate the refresh token, so the record is rewritten. Tokens are never printed, never written to any log or recording, never put on a command line, and never part of what the analyst sees (the analyst gets firewalled post text inside <untrusted> blocks, nothing else). If X answers 401, the header says login revoked or expired at X · dexterm x login and nothing is read until you log in again. You can also revoke at x.com/settings/connected_apps.

What is read, and when#

whatendpoint (user context)whencached
posts about the open coinGET /2/tweets/search/recent with $SYMBOL OR <mint> (-is:retweet -is:reply)every 60 s while the token screen is open, only newer posts after the first page15 min per query
your own X lists as KOL feedsGET /2/lists/:id/tweetsevery 5 min per enabled list15 min
manual KOL lists and packsGET /2/tweets/search/recent with from: handles, 20 per queryevery 5 min15 min
importing your listsGET /2/users/:id/owned_lists + GET /2/lists/:id/membersonly when you press X on the KOL screen or run dexterm kol import-x–
who you areGET /2/users/meonce at login–

Never: a filtered stream (economically unbounded), cashtag firehoses, search for every coin on the board, x.com pages, third-party mirrors. ⏎ on a post opens its URL in your browser; dexterm itself never fetches it.

Every response goes through the rate limiter (one bucket for X, and every x-rate-limit-* header updates a per-endpoint window) and the health registry; a 429 pauses reads until X's reset time and the header shows x-stream ◐ rate limited 5m. Posts then flow through the social layer exactly like news and on-chain rows: near-duplicates collapse, account bursts are marked spam, cashtag squats are hidden, instruction-like posts are withheld from the model and flagged INJ for you.

Free vs paid X API tiers (your bill, your limits)#

Login with X gives dexterm your per-user rate windows and compliant user-authorized access. It does not move cost anywhere: X's pay-per-use pricing bills the developer account that owns the app, and with this design that account is yours. Facts as published by X (check the console; they change):

tierwhat it means for dexterm
Freelogin and identity work (/2/users/me), but search and list timelines are not included: the app's tier answers 403 client-not-enrolled, dexterm shows x-stream ◐ app tier (no search) and reads nothing. KOL list import (owned_lists, members) is also tier-gated; expect it to fail on Free.
Pay-per-use / Basic and upsearch/recent (300 requests per 15 minutes per user, last 7 days) and list timelines (900 per 15 minutes) work. X bills about $0.005 per post read and $0.01 per user read, deduplicated per UTC day.

dexterm keeps its own estimate and a hard stop: the SOURCES panel (s on the trending board) shows @handle · 12/300 search · $0.12 of $10 today. The default daily budget is $10 (DEXTERM_X_DAILY_USD=5 lowers it; 0 means "never read X"); when it is reached the header says x-stream ◐ daily budget and reads resume at the next UTC day. The estimate uses X's list prices and counts a post only the first time its id is seen in a day; the X console is the actual bill, so set a spending limit there too. At the design cadence a watched coin costs roughly $6 a day and KOL list polling roughly $7 a day on Basic; the 15-minute cache, since_id and the 24-hour dedupe table are what keep it there.

Degraded states you will see#

headermeaningwhat happens
x-stream ○ logged outno loginkeyless venues, news and on-chain rows only
x-stream ●4s @youreadingthe age is the last successful read
x-stream ◐ rate limited 5mX answered 429 or the window is exhaustedreads resume after X's reset time
x-stream ◐ daily budget 3hyour dexterm budget is spentreads resume at the next UTC day
x-stream ◐ app tier (no search)the app is on the Free tieridentity only; upgrade the app in the X console
x-stream ◐ errortransport or 5xxretried with backoff, up to 10 minutes

Nothing is ever shown as fresh when it is not, and nothing is invented to fill a gap.

KOL lists and the per-handle filter#

K opens your lists. X there (or dexterm kol import-x) imports the lists your X account owns as X LIST entries: handles only, never posts, and they count as tier A for credibility like your own manual lists. o on a handle narrows the trending board and the SOCIAL panel to that handle's posts (press o again, or f, to clear). Muting a handle (m) drops its posts before they are stored.

Tests#

The whole flow runs in the test suite against a mock X server on 127.0.0.1: PKCE and state checks, single-use 30-second codes, refresh rotation, revoke, 401/429/403 and budget stops, list import, and the firewall on an injection tweet. No test ever contacts x.com, and the default suite blocks fetch entirely.