Skip to content

Commit d286d80

Browse files
chore: merge main into rewrite/main
Brings 142 commits from main onto the rewrite line, which had 30 of its own. Three files conflicted and each resolution keeps the side that matches the code this branch actually runs: - .github/workflows/checks.yml — union. The rewrite's path filters for resources/ and crates/ stay; main's two globs (.github/workflows/** and .github/scripts/**) are added to the src filter, because a unit test reads those files and #965 skipped the suite a release.yml change broke. - CONFIGURATION.md — the rewrite's mirroring.static[] rows stay. main removed the setting along with its TypeScript worker; this branch still parses it in nostream-config (settings.rs, guided.rs, validate.rs and the mirror_filters test), so main's "removed" row describes a worker that never existed here. - resources/default-settings.yaml — the rewrite's connection.maxConnectionsPerIp block stays, and main's comment about period units is kept above the rateLimits list it describes. The knex 3.2.9 bump from #1024 arrives with the merge, so the standalone PR that carried the same fix onto this branch is superseded.
2 parents 7f491f4 + 35244d2 commit d286d80

337 files changed

Lines changed: 34910 additions & 4233 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.changeset/knex-3-2-9-security.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"nostream": patch
3+
---
4+
5+
fix(deps): bump knex to 3.2.9 for the search_path SQL injection (AIKIDO-2026-10469)
6+
7+
knex 0.0.1–3.2.8 interpolates values passed to `searchPath` for the Postgres
8+
dialect into generated SQL without validating or escaping them, so a value an
9+
attacker can influence becomes an injected identifier. 3.2.9 escapes the double
10+
quotes (#6411); this repository declares `knex` 2.4.2 on both the default branch
11+
and `rewrite/main`.
12+
13+
nostream never sets `searchPath` — `grep -rn "search_path" src/` finds nothing —
14+
so the reachable path is not exercised today, and the bump is taken to remove a
15+
vulnerable dependency from the image, not to close a reachable hole.
16+
17+
knex 3.0.0 drops Node < 16, which the repo already requires (>=24.14.1), and 3.2.0
18+
tightens `RawBinding`. The one compile error that surfaced,
19+
`src/scripts/benchmark-queries.ts` passing `bindings as readonly unknown[]` to
20+
`db.raw()`, is fixed by dropping the assertion: `toSQL().bindings` is already the
21+
accepted type. The emitted JavaScript is byte-identical with and without the
22+
assertion.

‎.changeset/peering-peer-auth.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"nostream": minor
3+
---
4+
5+
feat(peering): authenticate peer relays with NIP-42 using the relay keypair
6+
7+
Adds the peering identity layer: a configured `peering.peers` trust list keyed
8+
by relay pubkey (hex or `npub`), outbound NIP-42 AUTH signed with the relay's own
9+
keypair, and inbound recognition of a verified peer pubkey on the connection
10+
state. Peer status is tied to the NIP-42 session, so it expires or clears with
11+
the socket; a valid AUTH from an unconfigured pubkey stays an ordinary client.
12+
13+
`peering.peers[].relayUrl` is the outbound address only — identity is always the
14+
verified pubkey, never the URL. Relay key storage and the AUTH exchange are
15+
documented in `docs/PEERING.md`, and the new fields are validated with the rest
16+
of the settings document.

‎.factory/config.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"integrationBase": "rewrite/main"
3+
}

‎.github/assets/logo.jpg‎

40.9 KB
Loading

‎.github/assets/logo.png‎

127 KB
Loading
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Run a wrangler deploy, retrying only a transient Cloudflare failure.
4+
#
5+
# .github/scripts/deploy-with-retry.sh <wrangler command...>
6+
#
7+
# The command is taken from the caller so each workflow keeps its own
8+
# invocation (`npx wrangler deploy --config wrangler.preview.jsonc` for a
9+
# preview, `pnpm exec wrangler deploy --config wrangler.router.jsonc` with
10+
# `--var` for production).
11+
#
12+
# Only three signals are retried, because retrying anything else triples the
13+
# noise and delays the message that matters:
14+
#
15+
# - `code: 10013`, the Cloudflare API's "unknown error" seen in #970
16+
# - an HTTP 5xx from the Cloudflare API
17+
# - a connection reset
18+
#
19+
# An expired per-attempt timeout is retried too: a hung upload is the case the
20+
# timeout exists for, and failing on the first one would defeat it.
21+
#
22+
# Authentication failures, a missing environment and wrangler validation
23+
# errors fail on the first attempt.
24+
#
25+
# Every attempt after the first is announced with a `::warning::` naming the
26+
# attempt and the reason, so the flake rate stays visible in the Actions UI
27+
# instead of disappearing behind a passing job.
28+
#
29+
# Environment:
30+
# DEPLOY_ATTEMPTS total attempts (default 3)
31+
# DEPLOY_ATTEMPT_TIMEOUT seconds allowed per attempt (default 180)
32+
# DEPLOY_BACKOFFS seconds to wait before each retry (default "10 30")
33+
set -uo pipefail
34+
35+
attempts=${DEPLOY_ATTEMPTS:-3}
36+
attempt_timeout=${DEPLOY_ATTEMPT_TIMEOUT:-180}
37+
read -r -a backoffs <<< "${DEPLOY_BACKOFFS:-10 30}"
38+
39+
if [ "$#" -eq 0 ]; then
40+
echo "usage: $0 <wrangler command...>" >&2
41+
exit 2
42+
fi
43+
44+
if ! [[ "$attempts" =~ ^[1-9][0-9]*$ ]]; then
45+
echo "DEPLOY_ATTEMPTS must be a positive integer, got '$attempts'" >&2
46+
exit 2
47+
fi
48+
49+
# `timeout` is coreutils: every GitHub runner has it, a stock macOS does not.
50+
# Falling back keeps the script usable from a laptop (the unit suite runs it),
51+
# and says so rather than silently dropping the bound. Bash 3.2 ships on macOS,
52+
# so an empty array cannot be expanded with "${timeout_cmd[@]}" under `set -u`.
53+
timeout_cmd=()
54+
if command -v timeout >/dev/null 2>&1; then
55+
timeout_cmd=(timeout --kill-after=10 "$attempt_timeout")
56+
elif command -v gtimeout >/dev/null 2>&1; then
57+
timeout_cmd=(gtimeout --kill-after=10 "$attempt_timeout")
58+
else
59+
echo "::warning::no 'timeout' or 'gtimeout' on PATH — deploy attempts are not bounded (install coreutils to restore the per-attempt limit)"
60+
fi
61+
62+
logs=$(mktemp -d)
63+
trap 'rm -rf "$logs"' EXIT
64+
65+
# Echo the transient reason found in an attempt's output, or return 1 when the
66+
# failure is one we must not retry.
67+
transient_reason() {
68+
local log=$1
69+
local http
70+
71+
if grep -qE 'code: ?10013' "$log"; then
72+
printf 'Cloudflare API error code 10013'
73+
return 0
74+
fi
75+
http=$(grep -oE 'HTTP 5[0-9][0-9]' "$log" | head -1 || true)
76+
if [ -n "$http" ]; then
77+
printf '%s from the Cloudflare API' "$http"
78+
return 0
79+
fi
80+
if grep -qiE 'ECONNRESET|connection reset|socket hang up' "$log"; then
81+
printf 'connection reset'
82+
return 0
83+
fi
84+
85+
return 1
86+
}
87+
88+
for (( attempt = 1; attempt <= attempts; attempt++ )); do
89+
log="$logs/attempt-$attempt.log"
90+
status=0
91+
echo "deploy attempt $attempt/$attempts: $*"
92+
# The whole attempt is bounded: an upload that stops making progress must not
93+
# hold the job until the workflow's own timeout. `--kill-after` covers a
94+
# process that ignores TERM. Without coreutils the bound is dropped and the
95+
# warning above says so, rather than turning every deploy into exit 127.
96+
${timeout_cmd[@]+"${timeout_cmd[@]}"} "$@" >"$log" 2>&1 || status=$?
97+
cat "$log"
98+
99+
if [ "$status" -eq 0 ]; then
100+
echo "deploy succeeded on attempt $attempt/$attempts"
101+
exit 0
102+
fi
103+
104+
reason=$(transient_reason "$log") || reason=
105+
if [ -z "$reason" ]; then
106+
if [ "$status" -eq 124 ] || [ "$status" -eq 137 ]; then
107+
reason="attempt timed out after ${attempt_timeout}s"
108+
else
109+
echo "::error::deploy failed on attempt $attempt/$attempts with a non-transient error (exit $status) — not retrying" >&2
110+
tail -n 30 "$log" >&2
111+
exit "$status"
112+
fi
113+
fi
114+
115+
if [ "$attempt" -ge "$attempts" ]; then
116+
echo "::error::deploy failed after $attempts attempts — last failure: $reason" >&2
117+
tail -n 30 "$log" >&2
118+
exit "$status"
119+
fi
120+
121+
delay=${backoffs[$((attempt - 1))]:-0}
122+
echo "::warning::wrangler deploy attempt $((attempt + 1))/$attempts after attempt $attempt/$attempts failed with: $reason — waiting ${delay}s"
123+
if [ "$delay" -gt 0 ]; then
124+
sleep "$delay"
125+
fi
126+
done

‎.github/workflows/checks.yml‎

Lines changed: 11 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ jobs:
4343
- '.nvmrc'
4444
- 'resources/default-settings.yaml'
4545
- '.github/workflows/checks.yml'
46+
# A unit test reads these workflows, so a change to any of them
47+
# must run the suite that asserts on it. #965 changed release.yml
48+
# and skipped the very tests that change broke.
49+
- '.github/workflows/**'
50+
# test/unit/scripts/deploy-with-retry.spec.ts runs the deploy
51+
# script, so a change to it must run the suite that asserts on it.
52+
- '.github/scripts/**'
4653
rust:
4754
- 'crates/**'
4855
- 'Cargo.toml'
@@ -123,7 +130,8 @@ jobs:
123130
if: |
124131
needs.changes.outputs.src == 'true' &&
125132
needs.lint.result == 'success' &&
126-
needs.build-check.result == 'success'
133+
needs.build-check.result == 'success' &&
134+
github.head_ref != 'changeset-release/main'
127135
steps:
128136
- name: Checkout
129137
uses: actions/checkout@v4
@@ -166,7 +174,8 @@ jobs:
166174
if: |
167175
needs.changes.outputs.src == 'true' &&
168176
needs.lint.result == 'success' &&
169-
needs.build-check.result == 'success'
177+
needs.build-check.result == 'success' &&
178+
github.head_ref != 'changeset-release/main'
170179
steps:
171180
- name: Checkout
172181
uses: actions/checkout@v4
@@ -293,60 +302,3 @@ jobs:
293302
run: pnpm install --frozen-lockfile
294303
- name: Check for changeset
295304
run: pnpm exec changeset status --since origin/${{ github.base_ref }}
296-
297-
publish-container-image:
298-
name: Build and push container image
299-
runs-on: ubuntu-latest
300-
needs:
301-
- changes
302-
- lint
303-
- build-check
304-
- test-units-and-cover
305-
- test-integrations-and-cover
306-
- post-tests
307-
if: >-
308-
always() && github.event_name == 'push' && github.ref == 'refs/heads/main' &&
309-
needs.changes.result == 'success' &&
310-
(
311-
needs.changes.outputs.src != 'true' ||
312-
(
313-
needs.lint.result == 'success' &&
314-
needs.build-check.result == 'success' &&
315-
needs.test-units-and-cover.result == 'success' &&
316-
needs.test-integrations-and-cover.result == 'success'
317-
)
318-
) &&
319-
needs.post-tests.result == 'success'
320-
env:
321-
REGISTRY: ghcr.io
322-
IMAGE_NAME: ${{ github.repository }}
323-
permissions:
324-
contents: read
325-
packages: write
326-
steps:
327-
- name: Checkout repository
328-
uses: actions/checkout@v4
329-
330-
- name: Log in to the Container registry
331-
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
332-
with:
333-
registry: ${{ env.REGISTRY }}
334-
username: ${{ github.actor }}
335-
password: ${{ secrets.GITHUB_TOKEN }}
336-
337-
- name: Extract metadata (tags, labels) for Docker
338-
id: meta
339-
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
340-
with:
341-
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
342-
tags: |
343-
type=raw,value=main
344-
type=sha,prefix=sha-
345-
346-
- name: Build and push Docker image
347-
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
348-
with:
349-
context: .
350-
push: true
351-
tags: ${{ steps.meta.outputs.tags }}
352-
labels: ${{ steps.meta.outputs.labels }}

0 commit comments

Comments
 (0)