X session

How log-cli talks to X: directly, with no proxy, through one curl_cffi client per account that lives in one engine host, one call at a time, spaced the way X's own web app spaces them, and signed with X's live operation ids. Rules 80, 81, 104, 105, 110 and 113.

Transport

REQ-XSESSION-01: An X account calls X directly, with no proxy

The X engine makes its twikit client without a proxy, and the spec log hands the engine carries no proxy. Facebook's proxy gate (rule 12) does not apply to X. Rule 80.

Checked by: code and SubQ

REQ-XSESSION-02: twikit goes out as curl_cffi impersonating Safari

twikit's HTTP transport is one curl_cffi AsyncSession pinned to one impersonation target (IMPERSONATE, safari17_0), with redirects not followed and the proxy kept in the session. httpx is never used. Rule 81.

Checked by: code and SubQ

One call at a time

REQ-XSESSION-03: One call per account at a time

Every X call except the X Chat watch takes the account's engine.lock before it acts, and holds it until the call is done. A call that waits more than 15 minutes for the lock is refused with accountBusy and sends nothing. Rule 104.

Checked by: code and SubQ

REQ-XSESSION-04: Sends are spaced 4 to 11 seconds, reads 0.8 to 2.5 seconds

After the account's last call, a send waits a random 4 to 11 seconds and a read a random 0.8 to 2.5 seconds before it goes out. The time of every call is recorded so the next one is spaced from it. Rules 104 and 110.

Checked by: code and SubQ

REQ-XSESSION-05: The transaction key is kept for 3 hours

The client's transaction key is saved in clientTransaction.json after each call and restored by a new client while it is under 3 hours old, so X's home page and signer are not loaded again for every call. Rule 104.

Checked by: code and SubQ

One session

REQ-XSESSION-06: Every X call goes through the account's one engine host

Every call from the CLI, the MCP, the hooks, the queue and the X Chat watch goes through the account's one engine host, started on the first call. Running the call inline is only for tests (LOG_TWITTER_HOST=inline). Rule 105.

Checked by: code and SubQ

REQ-XSESSION-07: The host keeps one client for the account

While hosting, the engine keeps one twikit client per account and hands the same client to every call with the same login, so the account has one connection, one cookie jar and one transaction key. Rule 105.

Checked by: code and SubQ

REQ-XSESSION-08: A second host cannot start

A host holds the account's host.lock. A second host for the same account cannot take it and exits without serving. Rule 105.

Checked by: code and SubQ

REQ-XSESSION-09: A call that cannot reach the host is refused, never run on its own

When the host does not start or does not answer, the call is refused with xSessionHostDown, nothing is sent, and no second session is opened in its place. Rule 105.

Checked by: code and SubQ

REQ-XSESSION-10: The host closes when idle for 30 minutes or when its code changes

The host closes after 30 minutes without a call, and closes when any of the engine's code changes (the X engine, the platform kit or vendored twikit), even while an X Chat watch is open on it, so the next call runs the new code. Rules 105 and 108.

Checked by: code and SubQ

REQ-XSESSION-11: A call to a restarting session waits for the new one

When the host is closing as a call reaches it, the call waits for the old host process to end and connects to the new one, rather than failing. Rule 113.

Checked by: code and SubQ

REQ-XSESSION-12: A closing session drops its connections and exits at once

When the host closes it releases its record and its lock, closes its server and closes every open connection, so an open X Chat watch cannot hold it half closed. Rule 113.

Checked by: SubQ

X's live operations

REQ-XSESSION-13: webClient writes X's live operation table

log twitter session webClient loads x.com/home through the account's one session, saves the page and its scripts, and writes X's live operation ids and feature switch values to xOperations.json. Rule 113.

Checked by: code and SubQ

REQ-XSESSION-14: Every GraphQL call uses X's live id and exactly X's switches

Every session loads xOperations.json, and each GraphQL call to x.com takes X's live query id for its operation and sends exactly the feature switches X's client sends for it, with X's values. Rule 113.

Checked by: code and SubQ

REQ-XSESSION-15: webClient also saves X Chat's own app

web_client in python/twitterApi/engine.py calls save_xchat_build after saving x.com's scripts. It reads XCHAT_EMBED_MANIFEST (https://chat.x.com/embed-v1.json) through the account's one session, takes the build from the manifest's remoteEntry name and publicPath (xchat_build_of, matching XCHAT_BUILD), lists remoteEntry.js and every script the manifest's exposes name except other languages' locale files (xchat_scripts_of, XCHAT_LOCALE), and saves each into the save's xchat folder at its path under the public path, sleeping a read's spacing before each. Its answer's xchat says whether it worked, the build and how many files. Rule 150.

Checked by: code and SubQ