Get X posting credentials¶
Let keryx auth x capture a token so keryx publish can post a
text-and-image announcement to an X account (spec 0060 D16). X uses standard
OAuth 2.0 with PKCE; the durable secret is a refresh token that rotates on
every use, and keryx writes the new one back each time it mints an access
token. Fifteen minutes, plus whatever X's developer sign-up asks of you.
What you need¶
- An X developer account at https://developer.x.com/ with a Project and an App. The free tier allows posting (1,500 posts a month at the time of writing); check its limits page before relying on it.
- The App's User authentication settings turned on.
1. Set up user authentication on the App¶
In the developer portal, open the App → User authentication settings → Set up:
- App permissions: Read and write.
- Type of App: Web App, Automated App or Bot (confidential client).
- Callback URI / Redirect URL:
https://localhost:3764/and, if you want the fallbacks,https://localhost:3765/…https://localhost:3769/. - Website URL: the blog is fine.
Save, then copy the OAuth 2.0 Client ID and Client Secret it shows.
platforms.x.client_id (config, non-secret) and X_CLIENT_SECRET
(environment, never committed). The Client Secret is shown once.
2. Sign keryx in¶
It opens the consent page (or prints the URL), captures the code on the https
loopback, swaps it with the client secret and the PKCE verifier for an access
token and a refresh token, and stores the refresh token (keychain, else the
accounts file). platforms.x.{enabled,redirect_uri} are written. In CI,
X_REFRESH_TOKEN in the environment is the token; because it rotates, an
unattended runner needs the write-back (auth.writeback.*) or it is dead
after one use.
3. What a post looks like there¶
- Text, hashtags on a new line, the link on a new line, held to 280
weighted characters together: X wraps every URL in t.co, so a link counts
as 23 whatever its length, and
approvecounts it that way too. - Images (up to four, JPEG or PNG under 5 MB) are uploaded through the chunked media flow, so no public URL is needed; alt text is set on each.
- No video here.
Refresh¶
keryx auth refresh rotates the refresh token (a refresh mints a new pair)
and persists the new one; a rejected token is reported as reauth-required.
The publisher itself does the same on every post, so a run that posts also
refreshes.