All user-visible bugs and enhancements should be recorded here.
- Release binaries now include the full
xurl chatXChat client on macOS (arm64/amd64) and Linux (amd64) — installing via Homebrew, npm, or a GitHub release tarball no longer prints the "not available in this build" stub on those platforms. The Linux amd64 binary is statically linked against musl, so it runs on both glibc and musl (e.g. Alpine) distros. Platforms without a chat-xdk library (Windows, Linux arm64/i386) still ship the graceful stub.
xurl chat— an end-to-end encrypted XChat client. Commands:keys status|restore|import,conversations,read,send,listen,download,rotate,add-members,mark-read, andtyping. Encryption, decryption, and signing happen locally via the chat-xdk library; the server only sees ciphertext.sendsupports encrypted attachments (--file) and threaded replies (--reply-to); reading and sending mark the conversation read automatically. xurl never generates or registers keys — an account must already have XChat keys from another client, brought to this machine via Juicebox PIN recovery or an exported key blob, and stored in~/.xurl/keys.yml(mode 600). Requires a cgo build on macOS (amd64/arm64) or Linux (amd64); prebuilt release binaries ship a stub explaining how to build with chat enabled.
~/.xurlis now a directory: tokens and app credentials live in~/.xurl/auth.yml, and XChat private keys live in~/.xurl/keys.yml. An existing single-file~/.xurlmigrates automatically (rename-based and non-destructive) on first use, and the existing legacy migrations still apply on top of the new layout: pre-v1.0 JSON-format token files are converted to YAML, and.twurlrcimport is unchanged. Older xurl binaries cannot read the new layout.
- [2026-07-15] Cut Release GitHub Actions workflow (
workflow_dispatch) to promoteCHANGELOG.md, commit + tag onmain, and publish (GitHub release, Homebrew, npm) in one run.
- [2026-07-15]
xurl auth oauth2no longer always warns that the "default" app has no client credentials when--appis omitted. The check usedGetApp("")(empty-key map lookup, always nil) instead of the real default app, so it false-alarmed even when the active default (e.g.app-2) had credentials. The warning now resolvesdefault_appand names that app correctly.
- [2026-06-29]
mcp --helpno longer contradicts the bridge's behavior. The v1.2.1 help text still said the bridge "never opens a browser itself; if no token exists it exits with that instruction", but v1.2.1 changed the bridge to open the browser for a first-run OAuth2 login when no token is cached. The help now documents that, and points remote/headless hosts toxurl auth oauth2 [--app NAME] --headless.
- [2026-06-29]
mcpbridge now runs the interactive browser OAuth2 login on first run when no token is cached (usingCLIENT_ID/CLIENT_SECRETfrom its environment), instead of failing fast. This lets the bridge authenticate with no prior xurl setup — e.g. straight fromnpx … mcp— and then caches/auto-refreshes the token. The MCP handshake is held until the login completes (set a generousstartup_timeout_secon the server), and login diagnostics stay on stderr so the stdout JSON-RPC channel is unaffected. On a headless host, authenticate out-of-band first withxurl auth oauth2 --headless.
- [2026-06-29]
install.shnow usesid -uinstead of the bash-only$EUIDto detect root, socurl ... | sh(POSIX/dash) installs to/usr/local/binas root instead of silently falling back to~/.local/bin. (#68) - [2026-06-29] npm install on Windows works again:
install.jsextracts the.zipwith PowerShell'sExpand-Archiveinstead of the Unixunzipcommand. (#56) - [2026-06-29]
whoami(anduser) now requestverified_typeandsubscription_type, so Premium/blue accounts are reported correctly instead ofverified: false. (#41) - [2026-06-29] OAuth2 token exchange and refresh now send client credentials with the correct auth style — HTTP Basic header for confidential clients (those with a secret),
client_idin the body for public clients — instead of relying on autodetection, which could fail against X withunauthorized_client: Missing valid authorization header. - [2026-06-29]
mcpbridge no longer launches a browser at startup: it still refreshes an existing token silently, but when none is available it fails fast with instructions (xurl auth oauth2 [--app NAME] [--headless]) instead of opening a browser mid-startup (which could hang an MCP client's handshake) and printing to the JSON-RPC stdout channel. OAuth2 diagnostics now go to stderr. - [2026-06-29]
mcpbridge no longer lets a strict client hang: a request that cannot be answered — transport failure, a failed token refresh/retry after a 401, or a response with an empty/non-JSON body — now gets a synthesized JSON-RPC error keyed to its id. Notifications (e.g.notifications/cancelled) are no longer head-of-line blocked behind an in-flight streaming response, large but valid JSON error bodies are forwarded whole instead of being truncated, the standalone server->client stream stops probing a non-event-stream200and only resets its reconnect backoff after a healthy stream, and stdin memory stays bounded when an oversized line is dropped. - [2026-06-25]
mcpbridge hardening: serialized token-store access (fixes a fatal data race when a token expires mid-session), strict newline-delimited-JSON stdout (SSE/JSON responses are validated and compacted, non-JSON keep-alives dropped), a forced token refresh on HTTP 401, cancelable stdin so SIGINT/SIGTERM shuts the bridge down, resilience to oversized input lines, a server->client stream that resets its backoff/supports stateless servers/retries 408 & 429, and a best-effort sessionDELETEon shutdown. - [2026-06-25]
media upload --waitnow also waits for animated GIFs (auto-detected astweet_gif), and a media type that cannot be detected — or is recognized but unsupported (e.g.application/pdf) — now fails with a clear message instead of guessingtweet_imageand getting an opaque API error. - [2026-06-25]
timeline--max-resultsminimum corrected to 1 (matches the reverse-chronological endpoint). - [2026-06-25] The raw-request "No URL provided" usage message now prints to stderr.
- [2026-06-25]
media upload --waitnow actually waits for processing and no longer always sends the trace header — thewaitForProcessingandtracearguments were passed in the wrong order. - [2026-06-25] Raw API requests now surface the real transport/auth error instead of printing
nullwhen a request fails before getting an HTTP response (e.g. DNS or connection failures). - [2026-06-25] Requests with no usable credentials, or an invalid
--authvalue, now fail with a clear authentication error instead of silently sending an unauthenticated request. - [2026-06-25]
xurl dmnow JSON-encodes message text correctly; quotes, backslashes, and newlines no longer produce a malformed request body. - [2026-06-25] OAuth2 expiry is stored correctly; a token returned without an expiry now refreshes on next use instead of being treated as never-expiring.
- [2026-06-25]
--max-resultsis clamped to each endpoint's accepted range for timeline, mentions, bookmarks, likes, following, followers, dms, and posts. - [2026-06-25]
fetchUsernamenow uses a 10s HTTP timeout, and PKCE verifier generation now handles RNG errors instead of ignoring them. - [2026-06-25]
webhook starthelp now references the correct-Ppretty-print flag and serves on an isolatedServeMux. - [2026-06-25]
.gitignorenow correctly ignores.DS_Store(a missing newline had merged it with a comment). - [2026-04-19 23:08:51 CEST] OAuth2 callback listeners now bind to the host and port derived from the effective redirect URI instead of always listening on
127.0.0.1:8080. Forlocalhost,xurlnow listens on both127.0.0.1and::1, which fixes browser-dependent loopback resolution failures while still supporting non-default callback paths. - [2026-04-19 23:08:51 CEST] The OAuth2 listener now starts listening before the browser opens, which removes a race where the browser could reach the callback URL before the local server was ready.
- [2026-04-19 23:08:51 CEST] OAuth2 token refresh no longer depends on
/2/users/mesucceeding. If username discovery fails,xurlkeeps the refreshed token instead of failing the request. - [2026-04-19 23:08:51 CEST] Shortcut commands that need the current user ID now fall back to
--usernamelookups when/2/users/meis unavailable. - [2026-04-19 23:08:51 CEST]
GetOAuth2Headernow consistently returns aBearerheader even when it has to trigger a fresh OAuth2 flow.
- [2026-06-29] Added
xurl auth oauth2 --headlessfor authenticating on remote/headless machines where the localhost OAuth callback is unreachable: xurl prints the authorization URL, you open it on any device and approve, then paste the resulting redirect URL (or just thecode) back at the prompt. No callback listener or local browser is required. (Closes the headless half of #62 / #40.) - [2026-06-25] OAuth2 tokens now refresh ~30s before expiry (clock-skew leeway) so a token handed to a caller does not expire in-flight; a new forced-refresh path backs the
mcpbridge's 401 recovery. - [2026-06-25]
xurl token's missing-token error now names the requested user, andtoken/mcperrors omit ANSI color when stderr is not a terminal (cleaner piped/logged output). The auto-generatedhelp/completioncommands now appear under the Management group. - [2026-06-25] Added
xurl token: prints a valid (refreshed, persisted) OAuth2 access token for the active app to stdout without opening a browser, so it can be scripted. Respects--appand-u/--username. - [2026-06-25] Added
xurl mcp [URL]: a stdio↔Streamable-HTTP MCP bridge for the hosted X API MCP server (defaulthttps://api.x.com/mcp). It injectsAuthorization: Bearer <token>, maintains the MCP session id, handles plain-JSON and SSE responses, refreshes the token in-process, and triggers the browser login on first run if needed. Usable from any MCP client vianpx -y @xdevplatform/xurl mcp. - [2026-06-29] The app-only token command is now
xurl auth app-only [TOKEN](named for the auth mode, not the "bearer" token scheme that OAuth2 user tokens also use), taking the token as an argument or from stdin via-. It removes the oldappvsappsconfusion and the redundantauth bearer --bearer-token. Back-compat:auth appandauth bearerremain aliases and--bearer-tokenis still accepted. - [2026-06-25]
xurl --helpnow groups subcommands into "Posting & Engagement", "Users & Social Graph", "Reading & Lists", and "Management" sections instead of one flat list. - [2026-06-25] Added
xurl posts USERNAMEto list a user's recent posts. - [2026-06-25]
xurl --versionis now supported in addition toxurl version. - [2026-06-25] Raw requests now default to
POSTwhen-dis supplied (curl-like), andmedia uploadauto-detects the media type and category from the file extension when they are not provided. - [2026-05-14 11:38:34 PDT] Documentation and the bundled
xurlskill now recommend authenticating registered apps withxurl auth oauth2 --app APP_NAMEand explain that omitting--appsaves the token to the current default app. - [2026-04-19 23:08:51 CEST] OAuth2 tokens can now be retained without a discovered username label when X’s
/2/users/melookup is unavailable. Status output makes that state visible as(unknown user)instead of silently dropping the token. - [2026-04-19 23:08:51 CEST] Repo documentation now describes the effective redirect URI as the source of callback host, port, and path, calls out explicit username authentication as the safer fallback when username discovery is unreliable, and documents the new stored
redirect_uribehavior. - [2026-04-19 23:08:51 CEST] Apps can now store a per-app
redirect_uriin~/.xurl,REDIRECT_URIfrom the environment still takes precedence, andxurl auth apps redirect-uri get/setplusauth apps update --redirect-urimake that configuration visible and editable from the CLI. - [2026-04-19 23:48:20 CEST] Documentation now records the confirmed X platform enrollment requirement behind
client-forbidden/client-not-enrolledread failures: moving the app to thePay-per-usepackage and theProductionenvironment fixed live/2/*reads after OAuth had already succeeded.