@volter/twin-xidentity 0.1.10 → 0.1.12

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.
Files changed (59) hide show
  1. package/README.md +42 -14
  2. package/dist/src/generated/surface.gen.json +1 -0
  3. package/dist/src/generated/ui.gen.json +1 -0
  4. package/dist/src/index.d.ts +4 -4
  5. package/dist/src/index.js +40 -25
  6. package/dist/src/manifest.d.ts +2 -0
  7. package/dist/src/manifest.js +179 -0
  8. package/dist/src/screens/authorize.d.ts +40 -0
  9. package/dist/src/screens/authorize.js +184 -0
  10. package/dist/src/screens/oauth1.d.ts +12 -0
  11. package/dist/src/screens/oauth1.js +123 -0
  12. package/dist/src/semantics/index.d.ts +5 -0
  13. package/dist/src/semantics/index.js +15 -0
  14. package/dist/src/semantics/oauth1.d.ts +8 -0
  15. package/dist/src/semantics/oauth1.js +142 -0
  16. package/dist/src/semantics/shared.d.ts +37 -0
  17. package/dist/src/semantics/shared.js +55 -0
  18. package/dist/src/semantics/tokens.d.ts +37 -0
  19. package/dist/src/semantics/tokens.js +271 -0
  20. package/dist/src/semantics/users.d.ts +22 -0
  21. package/dist/src/semantics/users.js +207 -0
  22. package/dist/src/xidentity-capabilities.js +87 -16
  23. package/dist/src/xidentity-conformance.js +4 -4
  24. package/dist/src/xidentity-connector.js +3 -0
  25. package/dist/src/xidentity-consent-ui.js +2 -2
  26. package/dist/src/xidentity-doors.d.ts +4 -0
  27. package/dist/src/xidentity-doors.js +103 -0
  28. package/dist/src/xidentity-oauth1.d.ts +27 -11
  29. package/dist/src/xidentity-oauth1.js +18 -309
  30. package/dist/src/xidentity-scopes.d.ts +1 -1
  31. package/dist/src/xidentity-scopes.js +9 -5
  32. package/dist/src/xidentity-server.d.ts +23 -17
  33. package/dist/src/xidentity-server.js +132 -72
  34. package/dist/src/xidentity-store.d.ts +85 -45
  35. package/dist/src/xidentity-store.js +160 -159
  36. package/dist/src/xidentity-twin.d.ts +38 -45
  37. package/dist/src/xidentity-twin.js +81 -863
  38. package/package.json +3 -3
  39. package/src/generated/surface.gen.json +1 -0
  40. package/src/generated/ui.gen.json +1 -0
  41. package/src/index.ts +44 -27
  42. package/src/manifest.ts +220 -0
  43. package/src/screens/authorize.ts +212 -0
  44. package/src/screens/oauth1.ts +131 -0
  45. package/src/semantics/index.ts +23 -0
  46. package/src/semantics/oauth1.ts +156 -0
  47. package/src/semantics/shared.ts +79 -0
  48. package/src/semantics/tokens.ts +278 -0
  49. package/src/semantics/users.ts +221 -0
  50. package/src/xidentity-capabilities.ts +89 -15
  51. package/src/xidentity-conformance.ts +4 -4
  52. package/src/xidentity-connector.ts +2 -0
  53. package/src/xidentity-consent-ui.ts +2 -2
  54. package/src/xidentity-doors.ts +131 -0
  55. package/src/xidentity-oauth1.ts +23 -294
  56. package/src/xidentity-scopes.ts +9 -5
  57. package/src/xidentity-server.ts +135 -76
  58. package/src/xidentity-store.ts +180 -196
  59. package/src/xidentity-twin.ts +100 -949
package/README.md CHANGED
@@ -17,7 +17,7 @@ X's OAuth 2.0 issues **opaque bearer tokens**: no `id_token`, no JWKS, no discov
17
17
  Identity is fetched from `GET /2/users/me` with the access token. That non-OIDC shape is the whole
18
18
  reason this vendor needs its own pack next to `googleoauth` — an integration built against an OIDC
19
19
  provider (id_token claims, `openid email profile` scopes) breaks against X in exactly the ways this
20
- twin reproduces: the scope model is X's own catalog (the OpenAPI's 24 scopes plus the announcement-added
20
+ twin reproduces: the scope model is X's own catalog (the OpenAPI's 26 scopes plus the announcement-added
21
21
  `users.email`, which gates `confirmed_email` — a recorded intra-vendor discrepancy), the refresh
22
22
  token only arrives with `offline.access`, PKCE is **required**, and the only identity read is the
23
23
  API call.
@@ -31,18 +31,45 @@ bundle and stylesheet the twin serves are **committed text** (`src/xidentity-con
31
31
  written from `client/` by `bun scripts/consent-clients.ts` and drift-gated by
32
32
  `scripts/consent-clients.test.ts`): the serve path reads constants and never runs a bundler.
33
33
 
34
+ ## A derived pack (Protocol 3)
35
+
36
+ The pack is derived ([architecture](../../../docs/contributing/architecture.md#protocol-3-the-derived-pack)) from
37
+ X's own OpenAPI document, X API v2 version 2.168 (`spec/openapi.json.gz`, the same bytes the `x` pack vendors;
38
+ provenance in `spec/SOURCE.md`), corrected by `spec/patches.json`: the `x` pack's corrections to the wire's names
39
+ (`tweet.fields`, `pinned_tweet_id`, `most_recent_tweet_id`), and X's OAuth endpoints the document does not list,
40
+ patched in from docs.x.com's Authorization Code Flow page and OAuth API reference. `bun scripts/derive-pack.ts
41
+ xidentity` writes the wire (`src/generated/`). `GET /2/users/me`, `POST /2/oauth2/token`, `POST /2/oauth2/revoke`,
42
+ `POST /oauth/request_token`, `POST /oauth/access_token` and `POST /1.1/oauth/invalidate_token` (and its `.json`
43
+ form) are semantics handlers (`src/semantics/`); every other operation of the document is the gap, X's 404
44
+ problem — in a World the `x` pack claims those paths on `api.x.com` and `api.twitter.com`. The authorize pages are
45
+ screens (`src/screens/`), and the World's doors are `src/xidentity-doors.ts`. `src/manifest.ts` declares the
46
+ state machines the handlers and screens ask on every move (a screen's settling, a code's redemption, a refresh
47
+ token's rotation and revocation, an access token's revocation, an OAuth 1.0a request token's life, an OAuth 1.0a
48
+ access token's invalidation).
49
+
50
+ **A credential is bookkeeping, kept by its SHA-256** — an authorization code, an access token, a refresh token, an
51
+ OAuth 1.0a request token and an OAuth 2.0 App's client secret live under `_` types, never as a subject id or a field
52
+ of an entry that could be deployed or read in a changeset. A World that ran the protocol 1 pack keeps its
53
+ credentials under the types they were written as; they still resolve, and the first move on one writes it as
54
+ bookkeeping. Two rows keep a secret by contract: the OAuth 1.0a App (`oauth1_app`) and access token
55
+ (`oauth1_token`), which the `x` pack reads by the token itself through the kernel's owner read
56
+ (`xidentity-oauth1.ts` states the row shapes); moving them is that contract's change, on both packs at once.
57
+
58
+ Not done by the Protocol 3 procedure yet: the customer life, the vendor's published examples and the report
59
+ (`journeys/`); the capability manifest stays the pack's T0.
60
+
34
61
  ## Coverage
35
62
 
36
63
  Partial and honest. The manifest (`src/xidentity-capabilities.ts`) is the **enumerated identity
37
64
  service-area** denominator, not a claim to enumerate the whole X vendor surface. It is authored
38
- top-down from X's own artefacts (all fetched 2026-08-21): the **X API
39
- v2 OpenAPI document** (2.167) for `/2/users/me`, the Problem error family and the 24-scope catalog
40
- with X's consent descriptions; the **docs.x.com OAuth 2.0 guides** for the authorize/token/refresh/
65
+ top-down from X's own artefacts (first fetched 2026-08-21): the **X API
66
+ v2 OpenAPI document** (2.167 then; 2.168 vendored now) for `/2/users/me`, the Problem error family and the scope
67
+ catalog with X's consent descriptions; the **docs.x.com OAuth 2.0 guides** for the authorize/token/refresh/
41
68
  revoke requests; the **docs.x.com rate-limit pages** for the `x-rate-limit-*` headers and the
42
69
  75/15-minute per-user figure; and the **official SDK sources** (`@xdevplatform/xdk`,
43
70
  `twitter-api-typescript-sdk`) for the token/revoke response shapes the docs never show as JSON.
44
- It currently reads **98 done / 160 covered** (62 `todo`, and the login leg is not an API surface: it is the seeded session, so it is not in that
45
- denominator). The `todo`s are real X identity
71
+ It currently reads **103 done / 162 covered** (59 `todo`, and the login leg is not an API surface: it is the seeded
72
+ session, so it is not in that denominator). The `todo`s are real X identity
46
73
  surface this twin does not model — most notably the **v1.1 app-only bearer flow**, OAuth 1.0a's
47
74
  timestamp window and nonce replay, the unmodelled `user.fields` (entities, withheld, subscription, the
48
75
  relational fields), expansion hydration — plus a family of **wire-pinning todos**: behaviours the
@@ -66,7 +93,8 @@ code TTL). Each such `done` names its evidence boundary in the manifest and its
66
93
  - **Vendor-shaped opaque credentials** — the twin's codes/tokens are base64url blobs whose decoded
67
94
  structure (`<opaque>:<unix-ms>:1:1:ac|at|rt:1`) matches what the docs' own examples decode to.
68
95
  - **The identity read** — `GET /2/users/me` with the OpenAPI's scope demands (`tweet.read` +
69
- `users.read`), the 24-value `user.fields` enum with the wire's invalid-parameter envelope, the
96
+ `users.read`), the spec's `user.fields`, `expansions` and `tweet.fields` / `post.fields` enums with the wire's
97
+ invalid-parameter envelope (a query parameter the operation does not take is refused the same way), the
70
98
  documented `x-rate-limit-*` headers, 75/15min per-user accounting, and the documented
71
99
  429 + legacy code 88 refusal. `user.fields=profile_image_url` is the seeded photo, or X's default
72
100
  avatar URL (`abs.twimg.com/…/default_profile_normal.png`) for a persona seeded without one — the
@@ -77,7 +105,7 @@ code TTL). Each such `done` names its evidence boundary in the manifest and its
77
105
  (`xidentity-sdk.integration.test.ts`); the OAuth legs its hardcoded hosts cannot re-aim are
78
106
  proven over real transport in the docs' own curl shapes.
79
107
 
80
- ### OAuth 1.0a (`src/xidentity-oauth1.ts`)
108
+ ### OAuth 1.0a (`src/semantics/oauth1.ts`, `src/screens/oauth1.ts`, `src/xidentity-oauth1.ts`)
81
109
 
82
110
  The three-legged flow as docs.x.com's API reference and "Obtaining access tokens using 3-legged
83
111
  OAuth flow" state it, which is what Postiz (`gitroomhq/postiz-app`) connects an X account with
@@ -112,9 +140,8 @@ World's, an app signs with its host's), request-token expiry, the login step `fo
112
140
 
113
141
  The twin **authenticates nobody**: no password, no 2FA, no risk engine. The
114
142
  x.com "signed in" session is a seeded persona row — `POST /_twin/session` switches it, which is the
115
- honest local equivalent of "the browser is already signed in". This is the **identity service-area**
116
- of the X vendor twin. Posts, timelines, DMs and the rest of the X content API are additional
117
- service-areas to add within this same pack; they are not a reason to create a second X vendor pack.
143
+ honest local equivalent of "the browser is already signed in". This is X's **identity** surface; posts,
144
+ timelines and media are the `x` pack's, which shares X's hosts with this one by path.
118
145
 
119
146
  ### Twin-only scaffolding (not vendor surface, not counted)
120
147
 
@@ -125,7 +152,7 @@ internal endpoint; the OAuth 1.0a screen's posts to `POST oauth/authorize`, abov
125
152
  permission), `POST /_twin/clients`, `POST /_twin/accounts`, `POST /_twin/session`,
126
153
  `POST /_twin/rate_limit` (arm a deterministic 429), and the `/_twin/assets/*` page assets. This
127
154
  list is the audit trail the conformance census deliberately excludes — keep it in lockstep with
128
- `twinControl`'s branches in `xidentity-twin.ts`.
155
+ the doors in `xidentity-doors.ts`.
129
156
 
130
157
  ## The three operations
131
158
 
@@ -134,10 +161,11 @@ list is the audit trail the conformance census deliberately excludes — keep it
134
161
  `liveXIdentityExecute(accessToken)`, the ONE place a real X request may be issued, guarded by the
135
162
  fail-closed rate budget (`xidentity-budget.ts`: X's own 75/15min scheme, live-fetched
136
163
  2026-08-21). The client registry and the grant's scope set are **not observable** (no API reads
137
- X Apps; X has no token introspection) — reasoned gaps, not fakes.
164
+ X Apps; X has no token introspection) — reasoned gaps, not fakes. The same read is the protocol 2
165
+ refresh adapter, `syncXIdentityFromRemote`, over the kernel's executor.
138
166
  - **write** — the default: local consent flows mint local credentials, no real calls.
139
167
  - **push** — structurally impossible (X exposes no write API for this surface) and reported as
140
- such, never faked.
168
+ such, never faked: `performXIdentityAction` answers every entry `performed: false`.
141
169
 
142
170
  ## Serving
143
171