@vzn/vx-dte-server 0.0.651 → 0.0.653
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/PROTOCOL.md +8 -2
- package/README.md +2 -1
- package/package.json +1 -1
- package/src/oidc.ts +10 -12
- package/src/relay.ts +18 -6
- package/src/serve.ts +5 -0
package/PROTOCOL.md
CHANGED
|
@@ -102,7 +102,12 @@ A participant sends:
|
|
|
102
102
|
fails, `n` then `null`.
|
|
103
103
|
- A frame that fails is answered `{ "n", "status", "error" }`; one that is
|
|
104
104
|
no JSON object, or none of the above, `400`; one over 1 MiB `413`,
|
|
105
|
-
answered by its `n` when the frame starts `{"n":`.
|
|
105
|
+
answered by its `n` when the frame starts `{"n":`. One past its
|
|
106
|
+
runtime's own limit (16 MiB on Bun, 32 MiB on Workers) never reaches
|
|
107
|
+
the relay: the runtime closes the socket `1009`.
|
|
108
|
+
- A send whose `body` the relay cannot pass (nested deeper than its
|
|
109
|
+
runtime serializes) is `400` in its result; its receiver keeps its
|
|
110
|
+
socket.
|
|
106
111
|
- A removed peer's socket is closed `4404` after what waited for it; the
|
|
107
112
|
host is not told it left.
|
|
108
113
|
|
|
@@ -113,7 +118,8 @@ socket, or a push that failed) is dropped. Delivery is the clients': a
|
|
|
113
118
|
sender numbers each send, the receiver acknowledges it end to end, and
|
|
114
119
|
the sender sends again what is not acknowledged, at once on a new socket.
|
|
115
120
|
How they number and acknowledge is inside `body`, their own protocol;
|
|
116
|
-
`@vzn/vx-dte`
|
|
121
|
+
`@vzn/vx-dte` sends `{ "n", "b", "a"? }`: its number, its payload and
|
|
122
|
+
its ack.
|
|
117
123
|
|
|
118
124
|
Only the relay's notices wait, in memory, for the participant's next
|
|
119
125
|
socket.
|
package/README.md
CHANGED
|
@@ -163,4 +163,5 @@ The same relay as a `fetch` handler, to mount in a server of your own
|
|
|
163
163
|
repository: `repository` for GitHub Actions, `project_path` otherwise.
|
|
164
164
|
- `workflowClaimOf(iss)` names the claim that names the workflow a
|
|
165
165
|
token's job is defined in: `job_workflow_ref` for GitHub Actions,
|
|
166
|
-
`ci_config_ref_uri` otherwise.
|
|
166
|
+
`ci_config_ref_uri` otherwise. The relay matches it up to its last `@`,
|
|
167
|
+
before the ref or SHA.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx-dte-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.653",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "The reference relay for @vzn/vx-dte: carries messages between a vx run's orchestrator and its CI agents, admitting only CI OIDC tokens of the same repository.",
|
|
6
6
|
"keywords": [
|
package/src/oidc.ts
CHANGED
|
@@ -127,7 +127,7 @@ export function parseAllow(list: string): Allowed[] {
|
|
|
127
127
|
})
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
/** What `workflowClaimOf(iss)` says, up to its `@`, for a job of `workflow` in `repository`. */
|
|
130
|
+
/** What `workflowClaimOf(iss)` says, up to its last `@`, for a job of `workflow` in `repository`. */
|
|
131
131
|
function workflowPath(iss: string, repository: string, workflow: string): string {
|
|
132
132
|
return iss === GITHUB
|
|
133
133
|
? `${repository}/.github/workflows/${workflow}`
|
|
@@ -232,17 +232,15 @@ export function createVerifier(opts: OidcOptions): (token: string) => Promise<Ve
|
|
|
232
232
|
const ref = claims[claim]
|
|
233
233
|
if (typeof ref !== 'string') throw new TokenError(`no ${claim} claim`, 403)
|
|
234
234
|
const repo = String(claims[repositoryClaimOf(iss)])
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
)
|
|
245
|
-
}
|
|
235
|
+
// Cut at the last `@`, as vx-devtools does: the ref after it is
|
|
236
|
+
// `refs/…` or a SHA (a reusable workflow pinned by one), and GitLab's
|
|
237
|
+
// CI file may itself hold one (`.gitlab-ci.yml@group/project`, a file
|
|
238
|
+
// kept in another project, which a match by the prefix `<path>@` took
|
|
239
|
+
// as this project's own). A ref holding an `@` is refused, never widened.
|
|
240
|
+
const at = ref.lastIndexOf('@')
|
|
241
|
+
const workflow = at < 0 ? ref : ref.slice(0, at)
|
|
242
|
+
if (!ofRepo.some((a) => same(workflow, workflowPath(iss, a.repository ?? repo, a.workflow!))))
|
|
243
|
+
throw new TokenError(`workflow ${workflow} is not in this relay's ALLOW`, 403)
|
|
246
244
|
}
|
|
247
245
|
const run = (opts.runClaims?.[iss] ?? runClaimsOf(iss)).map((c) => {
|
|
248
246
|
const v = claims[c]
|
package/src/relay.ts
CHANGED
|
@@ -198,10 +198,16 @@ export function createRelay(opts: RelayOptions): Relay {
|
|
|
198
198
|
* relay's own notices (`notice`: joined, lost, closed) have no sender to
|
|
199
199
|
* resend them, so they wait for the next socket.
|
|
200
200
|
*/
|
|
201
|
-
const deliver = (
|
|
201
|
+
const deliver = (
|
|
202
|
+
session: Session,
|
|
203
|
+
p: Participant,
|
|
204
|
+
m: Message,
|
|
205
|
+
notice = false,
|
|
206
|
+
text?: string,
|
|
207
|
+
): void => {
|
|
202
208
|
if (p.socket !== undefined && p.queue.length === 0) {
|
|
203
209
|
try {
|
|
204
|
-
p.socket.send(JSON.stringify({ messages: [m] }))
|
|
210
|
+
p.socket.send(text ?? JSON.stringify({ messages: [m] }))
|
|
205
211
|
return
|
|
206
212
|
} catch {
|
|
207
213
|
// quiet: a socket that cannot send is unplugged; the participant rejoins
|
|
@@ -443,10 +449,16 @@ export function createRelay(opts: RelayOptions): Relay {
|
|
|
443
449
|
? session.byId.get(body.to)
|
|
444
450
|
: undefined
|
|
445
451
|
if (to === undefined || to === from) throw new HttpError(404, 'unknown to')
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
452
|
+
const m = { from: from.role === 'host' ? 'host' : from.id, body: body.body }
|
|
453
|
+
// Serialized before the push: a body parsed but nested past what the
|
|
454
|
+
// runtime serializes threw in it, and that unplugged the receiver.
|
|
455
|
+
let text: string
|
|
456
|
+
try {
|
|
457
|
+
text = JSON.stringify({ messages: [m] })
|
|
458
|
+
} catch {
|
|
459
|
+
throw new HttpError(400, 'body nests too deep to pass')
|
|
460
|
+
}
|
|
461
|
+
deliver(session, to, m, false, text)
|
|
450
462
|
}
|
|
451
463
|
|
|
452
464
|
const live = (key: string): Session => {
|
package/src/serve.ts
CHANGED
|
@@ -39,6 +39,11 @@ export function serveRelay(opts: ServeOptions): RelayServer {
|
|
|
39
39
|
idleTimeout: 60,
|
|
40
40
|
fetch: (req) => relay.fetch(req),
|
|
41
41
|
websocket: {
|
|
42
|
+
// Bun's default, said here because PROTOCOL.md names it: past it Bun
|
|
43
|
+
// closes the socket 1009 before the relay sees the frame (Workers
|
|
44
|
+
// does so past 32 MiB). Under it a frame over 1 MiB is a 413; more
|
|
45
|
+
// would only hold more of a sender's bytes per socket.
|
|
46
|
+
maxPayloadLength: 16 << 20,
|
|
42
47
|
// A socket with no frame in three heartbeats has no peer listening
|
|
43
48
|
// either (each probes a quiet one within two): closed, it loses its
|
|
44
49
|
// participant, as a half-open one on no other host would be.
|