@volter/twin-x 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,14 +29,14 @@ bun packages/twin/x/src/cli.ts serve --root /tmp/world
29
29
  | `GET /2/users/:id/timelines/reverse_chronological` | The home timeline — the account plus the accounts it follows; refused for anyone else's id. |
30
30
  | `GET /2/users/:id` · `GET /2/users/by/username/:username` | User lookup (handles are case-insensitive). An unknown user is X's partial-error 200 with a `resource_type: "user"` row, never a fabricated account. `GET /2/users/me` is `xidentity`'s and is not claimed here. |
31
31
  | `GET /2/tweets/search/recent` | Recent search over the local corpus: keywords, quoted phrases and `from:`. Grammar the twin does not model (`OR`, negation, grouping, other operators) is **refused**, never silently ignored. |
32
- | projection | `tweet.fields`, `user.fields`, `media.fields` and `expansions` (`attachments.media_keys` included) + the `includes` sidecar. The default is X's three fields (`id`, `text`, `edit_history_tweet_ids`) and nothing else — a documented field the twin holds no state for is refused by name rather than quietly omitted. |
32
+ | projection | `tweet.fields`, `user.fields`, `media.fields` and `expansions` (`attachments.media_keys` included) + the `includes` sidecar. The default is X's three fields (`id`, `text`, `edit_history_tweet_ids`) and nothing else — a documented field the twin holds no state for is refused by name rather than quietly omitted. `user.fields=profile_image_url` is the photo seeded for the account, or X's default avatar URL for one without (the twin serves the URL, not the picture). |
33
33
  | paging + windows | `pagination_token` / `meta.next_token`, `max_results` (5..100 on the timelines, 10..100 on search), and `since_id` / `until_id` / `start_time` / `end_time` with X's own id-beats-time precedence. |
34
- | auth | The bearer `xidentity`'s OAuth flow mints, its scope set, and X's `about:blank` 401 / `Forbidden` 403 problems. |
34
+ | auth | The bearer `xidentity`'s OAuth 2.0 flow mints and its scope set, or an **OAuth 1.0a User Context** signature (HMAC-SHA1) over an access token `xidentity`'s three legs issued — a Read-only App's token reads and gets X's `oauth1-permissions` 403 on a write; X's `about:blank` 401 / `Forbidden` 403 problems. |
35
35
  | errors | `resource-not-found`, `invalid-request`, the 429 + legacy code 88 pair, `405` under `--read-only`. |
36
36
  | connector | Pull **both** timelines — mentions and the account's own posts — following `next_token` to the end of each; **push** a pending post/quote/reply/delete to the real vendor, confirmed under the id X minted. A post with media is **performed** by uploading each file to X first (an image in one request; a video chunked, then polled on STATUS until processed) and posting with the media ids X minted. |
37
- | `/_twin/*` | Twin-only control plane: seed an account (its profile — `description`, `location`, `url`, `protected`, `verified` — and its long-post entitlement), a bearer, an inbound mention, a follow edge, or an armed rate refusal. |
37
+ | `/_twin/*` | Twin-only control plane: seed an account (its profile — `description`, `location`, `url`, `profile_image_url`, `protected`, `verified` — and its long-post entitlement), a bearer, an inbound mention, a follow edge, or an armed rate refusal. |
38
38
 
39
- `src/x-capabilities.ts` is the denominator: **114 capabilities, 74 done**. What is
39
+ `src/x-capabilities.ts` is the denominator: **125 capabilities, 83 done**. What is
40
40
  not modelled is enumerated there as a todo — polls, hide-reply, `entities`/`public_metrics`,
41
41
  the `user.fields` with no state behind them, full-archive search and the query grammar, likes/reposts/bookmarks, stream rules, the
42
42
  filtered and sampled **streaming** connections, GIFs, alt text, a video's real poster frame and X's transcoded
@@ -72,10 +72,24 @@ on those exact fields to decide what it writes (`conversation_id`, `in_reply_to_
72
72
 
73
73
  ## Relationship to `@volter/twin-xidentity`
74
74
 
75
- `xidentity` owns **X's OAuth 2.0** — the x.com authorize screen, the authorization-code + PKCE
76
- round trip, and `GET /2/users/me`. This pack owns the **posting surface** and consumes the user
77
- access token that flow mints. It never models OAuth: X's authorization-code round trip is a
78
- capability of `xidentity`, and duplicating it here would be two accounts of one vendor's auth.
75
+ `xidentity` owns **X's OAuth** — the OAuth 2.0 authorize screen and authorization-code + PKCE
76
+ round trip, the OAuth 1.0a three legs (`oauth/request_token`, the `oauth/authorize` /
77
+ `oauth/authenticate` screen, `oauth/access_token`) and `GET /2/users/me`. This pack owns the
78
+ **posting surface** and consumes the credentials those flows mint. It never models OAuth:
79
+ duplicating it here would be two accounts of one vendor's auth.
80
+
81
+ **One credential store, read across the seam.** X has one store of OAuth 1.0a access tokens, and
82
+ it is `xidentity`'s. An access token a person approved there must also sign a post here, so
83
+ `src/x-oauth1.ts` reads `xidentity`'s `oauth1_token` and `oauth1_app` rows through the kernel
84
+ projection (read-only; the row shapes are the contract `xidentity-oauth1.ts` states) and verifies
85
+ the signature with a transcription of its RFC 5849 verifier. The token acts for its `user_id`,
86
+ which is the X account in this pack's state: a World that posts as a person seeds that account
87
+ (id and handle) here, as it does for a `/_twin/tokens` bearer. The World's own App — the
88
+ `X_API_KEY` / `X_API_SECRET` the World sets (Postiz's names; `TWITTER_API_KEY` /
89
+ `TWITTER_API_SECRET` also) — is read with `worldEnvValue` on both sides.
90
+ `src/x-oauth1.integration.test.ts` runs both packs on one root (xidentity from its own bin) and
91
+ drives Postiz's path with `twitter-api-v2`: the three legs, `v2.me()`, `v2.uploadMedia`, a post, and
92
+ Postiz's own `signOAuth1`.
79
93
 
80
94
  The two agree about X rather than sharing code: `scripts/architecture.test.ts` (A3) forbids a
81
95
  cross-vendor pack import, so `src/x-problems.ts` and `src/x-scopes.ts` **transcribe** xidentity's