main
tag: launch-2026-06-01
tag: magicmirror-v0.1.1
tag: magicmirror-v0.3.0
tag: mnw-cli-v0.1.2
tag: mnw-cli-v0.1.3
tag: mnw-cli-v0.1.4
tag: pom-v0.4.1
tag: pom-v0.4.2
tag: pom-v0.4.3
tag: pom-v0.4.4
tag: pom-v0.4.5
tag: wam-v0.3.0
tag: wam-v0.3.1
Files
Commits
Tags
Notes
Issues
Send the SDK key the token grant requires, and record testnot's missing secret
Second wall behind the content type: the server calls validate_synckit_key on
the authorization_code grant and rejects an empty one with a 422, while
server/docs/oauth_integration.md step 3 does not list the field at all. mt
uploads nothing through SyncKit, so it sends its client id - one billing slot
per registered instance, and the refresh grant carries the stored key forward.
Third was testnot itself: no SYNCKIT_JWT_SECRET, so /oauth/token answered 503
for everyone. Set on the box, and written down here so a rebuilt testnot does
not lose it silently.
The harness walk now passes end to end against astra, before and after
reset-astra.sh.
3 files changed,
+19 insertions,
-0 deletions
174
174
`mnw-testnot-seed.sh` already passes through. Neither value is in the repo: one
175
175
is a credential, the other names a tailnet host.
176
176
177
+
`SYNCKIT_JWT_SECRET` has to be set on that box too, and it was not until
178
+
2026-08-15. It is the HS256 key `/oauth/token` signs with, and without it every
179
+
token exchange answers 503 "SyncKit is not configured" — a login outage that
180
+
looks like a transport failure from mt's side, since mt retries a 5xx twice
181
+
before giving up. The seed does not set it (it is a machine secret, not seed
182
+
data), so a rebuilt testnot needs it added by hand: 32 characters minimum, or
183
+
the server refuses to boot.
184
+
177
185
The three accounts carry fixed MNW ids, and mt's own seed (`src/seed.rs`)
178
186
assigns their community roles by `mnw_account_id` — Owner and Moderator for
179
187
`harness_owner` — because login upserts identity and perks but never
534
534
("redirect_uri", config.oauth_redirect_uri.as_str()),
535
535
("code_verifier", verifier),
536
536
("client_id", config.oauth_client_id.as_str()),
537
+
// The server's `key` is the SDK key naming the billing slot an
538
+
// integration's uploads count against, and it rejects an empty
539
+
// one on this grant even though the published contract
540
+
// (server/docs/oauth_integration.md step 3) does not list it.
541
+
// mt uploads nothing through SyncKit, so the client id is the
542
+
// honest answer: one slot per registered instance, and the
543
+
// refresh grant carries the stored key forward without asking.
544
+
("key", config.oauth_client_id.as_str()),
537
545
])
538
546
.send()
539
547
.await;
194
194
.and(body_string_contains("grant_type=authorization_code"))
195
195
.and(body_string_contains("code=the-code"))
196
196
.and(body_string_contains("code_verifier="))
197
+
// Required by the server on this grant, absent from its published
198
+
// contract, and a 422 when it is missing.
199
+
.and(body_string_contains("key="))
197
200
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
198
201
"access_token": "acc", "token_type": "Bearer", "expires_in": 300,
199
202
"refresh_token": "rt", "scope": "profile:read perks:read offline_access",