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#
- An X developer account at developer.x.com (log in with the X account you want to read as).
- 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).
- 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 requires127.0.0.1, notlocalhost, and an exact match), any website URL, and a name/description for the consent screen. - 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 recorddexterm 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#
| what | endpoint (user context) | when | cached |
|---|---|---|---|
| posts about the open coin | GET /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 page | 15 min per query |
| your own X lists as KOL feeds | GET /2/lists/:id/tweets | every 5 min per enabled list | 15 min |
| manual KOL lists and packs | GET /2/tweets/search/recent with from: handles, 20 per query | every 5 min | 15 min |
| importing your lists | GET /2/users/:id/owned_lists + GET /2/lists/:id/members | only when you press X on the KOL screen or run dexterm kol import-x | – |
| who you are | GET /2/users/me | once 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):
| tier | what it means for dexterm |
|---|---|
| Free | login 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 up | search/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#
| header | meaning | what happens |
|---|---|---|
x-stream ○ logged out | no login | keyless venues, news and on-chain rows only |
x-stream ●4s @you | reading | the age is the last successful read |
x-stream ◐ rate limited 5m | X answered 429 or the window is exhausted | reads resume after X's reset time |
x-stream ◐ daily budget 3h | your dexterm budget is spent | reads resume at the next UTC day |
x-stream ◐ app tier (no search) | the app is on the Free tier | identity only; upgrade the app in the X console |
x-stream ◐ error | transport or 5xx | retried 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.