Markdown · llms.txt

X app setup

Human required. A person creates this app in a browser. An agent cannot click through the X developer portal for you.

Bookmarks are readable only with OAuth 2.0 user context. An app-only bearer token gets a 403 on that endpoint, which is why tweet-factory dropped the old OAuth 1.0a path.

In the developer portal

  1. Open developer.x.com and create an app (the free tier is enough for read access).
  2. Under User authentication settings set:
    • App permissions: Read
    • Type of App: Web App, Automated App or Bot (this is a confidential client, which is what returns a refresh token)
    • Callback URI: http://127.0.0.1:8675/callback
    • Website URL: any URL you control. The CLI does not call it.
  3. Save.
  4. Open Keys and tokens and copy the OAuth 2.0 Client ID and Client Secret into .env as TWITTER_OAUTH2_CLIENT_ID and TWITTER_OAUTH2_CLIENT_SECRET.

The callback must match exactly, including the port 8675 and the path /callback. tf auth binds 127.0.0.1 unless you change it.

Scopes the CLI requests

tweet.read, users.read, like.read, bookmark.read, offline.access.

offline.access is the refresh token. Without it, the access token expires and every later fetch needs a new browser login. A public client (native app) often will not return a refresh token; use the confidential web-app type and keep the client secret.

Authorize

bash
./tf auth

A browser opens. Log in as the account whose likes and bookmarks you want, and click Authorize app. The CLI writes .tokens.json (mode 0600) and prints the granted scopes plus whether a refresh token is present.

Headless:

bash
./tf auth --no-browser

Open the printed URL on a machine with a browser. The callback still has to hit the process that is waiting. From another machine, set OAUTH_BIND_HOST and OAUTH_REDIRECT_URI to a reachable URL, register that exact URI on the app, and pass the same values (--bind, --redirect-uri). The default wait is 5 minutes (--timeout seconds).

Verify. .tokens.json exists, is non-empty, and the auth log said Has refresh token: true. File mode is 600 (stat -c '%a' .tokens.json).

One machine

The refresh token rotates every time it is used. Run tf fetch / tf refresh / tf cron on a single machine. A second checkout that refreshes the same token invalidates the first, and the next call returns 401. Fix that with one tf auth on the machine you intend to keep.

Verify. After auth, ./tf fetch prints a database summary with like, bookmark, and own counts (a brand-new account may legitimately have zeros).