Commit Graph

526 Commits

Author SHA1 Message Date
Krystie 53f003aef1 v5.9.24: update TRI home + explorer links, networking fixes, checkpoint publisher
- qt: TRI home → https://cryptographic-triangles.org/ (UI + 65 locales)
- qt: block explorer → https://blocks.cryptographic-triangles.org (65 locales)
- net: networking hardening + checkpoint publisher support
- build: MinGW cross-compilation toolchain, CI tridock rebuild trigger
- test: checkpoint publisher + onion v3 test updates
- test: chaindb equivalence test suite (LevelDB↔RocksDB migration parity)
- util: expose ResetDataDirCache() for test fixture datadir switching
- txdb: WriteRawPublic/ReadRawPublic test seam for raw byte-level access
- version bump 5.9.23 → 5.9.24
v5.9.24
2026-06-23 20:13:02 -07:00
Krystie 9762c741b7 distribute: fix $schema aka.ms URL + add NSIS Silent switches
Two errors from PR #391813 manifest validation (build 349844):

1. 'The schema header URL does not match the expected pattern.'
   I used raw.githubusercontent.com URLs, but the validator wants
   the aka.ms short URLs that the official winget-bot uses.
   Updated all 3 files to https://aka.ms/winget-manifest.*.1.12.0.schema.json

2. 'Silent and SilentWithProgress switches are not specified for
   InstallerType exe.'
   TrianglesQt installer is built with NSIS (see build-all.yml
   'Install NSIS via MSYS2' step + mingw-w64-x86_64-nsis package).
   NSIS silent flag is /S. Added both Silent and SilentWithProgress.

Closes superseded PR microsoft/winget-pkgs#391813 (same Manifest-Validation-Error).
2026-06-22 21:33:13 -07:00
Krystie 6726365872 distribute: fix $schema heredoc escaping + INSTALLER_URL ${{ }} substitution
Two pre-existing latent bugs in the WinGet job template:

1. The line '# yaml-language-server: $schema=...' was inside a
   <<EOF heredoc, so bash treated $schema as an undefined variable
   and stripped it down to '=https://...'. The resulting YAML still
   parsed (since the $schema line is just an editor comment), but
   IDE auto-complete and editor-side validation were broken.

   Fix: escape the $ as \$ in the heredoc so bash leaves it alone.

2. INSTALLER_URL was set in the workflow env: block with literal
   ${VERSION} placeholders. GitHub Actions only substitutes \${{ }}
   expressions in env values, not ${}. So the bash $VERSION got
   expanded but the URL kept ${VERSION} literal in the output —
   meaning the published manifest had a broken InstallerUrl that
   the Microsoft validator would 404 on (and a literal ${VERSION}
   string in SHA-source comparison).

   Fix: use ${{ env.VERSION }} in the workflow YAML so GitHub Actions
   substitutes it at runtime. Then bash gets the real version string
   and the heredoc just expands the resulting env var.
2026-06-22 20:57:16 -07:00
Krystie 20fc2ee6dd distribute: bump WinGet manifest schema 1.6.0 → 1.12.0
The winget-pkgs repository has tightened its accepted schema. Per
doc/ValidationFailureGuide.md:
- 'Manifest-Version-Deprecated: Update your manifest to use a supported
   schema version. The recommended schema version is 1.12.0
   (1.10.0 is also accepted).'
- 'Manifest-Validation-Error: Address all reported errors and resubmit.'

What changed in the template heredocs:

1. ManifestVersion: 1.6.0 → 1.12.0 in all 3 files
2. Version file: dropped Publisher/PublisherUrl/PackageName/License/
   ShortDescription (those belong in defaultLocale only).
   Replaced PackageLocale: en-US with DefaultLocale: en-US — that
   field was renamed in schema 1.12.
3. Installer file: replaced InstallerMode: interactive with
   InstallModes: [interactive, silent] (the singular 'InstallerMode'
   was removed; InstallModes is now an array per-installer or root).
   Dropped PackageLocale (not part of installer schema) and
   InstallerScope: user (no longer supported at root, only per-installer).
4. Added # yaml-language-server: $schema=... comment to all 3 files
   pointing at the official 1.12.0 JSON schemas — helps editor/IDE
   auto-complete AND validates against the same schema the winget
   validators use.

Supersedes PR microsoft/winget-pkgs#391801 (closed in same batch —
manifests there used the 1.6.0 schema and got Manifest-Validation-Error).
2026-06-22 20:47:14 -07:00
Krystie 5d9a0f47f9 distribute: add WinGet spam-safeguards (pre-flight + watchdog)
Sami's winget-pkgs submission bot has been firing one PR per release.
Three of them (#391151/391368/391388) were generated with a buggy path
format and accumulated PullRequest-Error / Needs-Author-Feedback labels
before Sami noticed. That pattern reads as spam to winget-pkgs moderators
and risks the maintainer goodwill we've built with stephengillie.

Two new safeguards:

1. Pre-flight check (distribute.yml, winget job):
   - Before opening a PR, scan existing SamiAhmed7777 PRs on
     microsoft/winget-pkgs for PullRequest-Error or
     Needs-Author-Feedback labels
   - If any are found, abort this submission with a clear error
   - Also skip if a PR for this exact version is already open

2. New winget-watchdog.yml workflow (cron */30 * * * *):
   - Every 30 min, scan open SamiAhmed7777 PRs
   - For each one, inspect wingetbot comments for validation result
   - If a PR has automatic-validation failure comments, post a
     summary comment + close the PR automatically
   - This prevents 'broken PR opened, forgotten for 24h' pattern
     that creates the spam appearance

Both changes keep the existing tag-triggered release flow intact.
2026-06-22 20:36:06 -07:00
Krystie 7f309800e5 distribute: fix WinGet manifest path casing + folder structure
PUBLISHER_INITIAL was hardcoded to 'C' but the winget-pkgs convention
requires lowercase 'c' for the first-letter prefix folder. Additionally,
the manifest was being placed at manifests/c/CryptographicTriangles/<full
PackageIdentifier with dot>/<version>/, but the correct convention is
manifests/c/CryptographicTriangles/<short package name>/<version>/ — the
file *names* still use the full PackageIdentifier (e.g.
CryptographicTriangles.TrianglesQt.installer.yaml).

Without these fixes, microsoft/winget-pkgs Automatic Validation rejects
the PR with: "the casing of the file in disk or identical file is not
merged" because the path written to the (Windows, case-insensitive)
validator filesystem differs from what's in the git tree.

Closes superseded PRs microsoft/winget-pkgs#391151, #391368, #391388.
2026-06-22 20:13:05 -07:00
Sami Ahmed ff0eeaac89 net: harden v5.9.22 networking changes — strict parser, tests, debug logs
Three pure helper functions extracted from ThreadHTTPSeedFetch2 into
netbase.{h,cpp} so the HTTPS seed-list code path can be unit-tested
without the SSL/Tor network stack:

  int DechunkTransferEncoding(const std::string& body, std::string& out)
  std::vector<std::string> ParseSeedListBody(const std::string& body)
  bool IsValidSocksNegotiationTimeout(int nMs)

DechunkTransferEncoding is now strict (was lenient):

  - Hex validation: every byte of the chunk-size line is checked with
    isxdigit() before strtoull. Old code passed a raw strtoul() result
    which silently accepted leading '+', '-', and whitespace.
  - strtoull + errno + size_t bounds check replaces the silent
    'if (pos+chunkSize > body.size()) chunkSize = body.size()-pos'
    clamp. The old behavior would mask truncated network reads.
  - Empty size lines, '+5' / '-5' / ' 5', and unsigned overflow all
    return DECHUNK_INVALID_HEX (or DECHUNK_OVERSIZE_CHUNK for the
    bounds case) instead of being treated as 0/last-chunk.
  - Missing CRLF after chunk data returns DECHUNK_MISSING_DATA_CRLF
    rather than being read as the next chunk-size line.
  - Body without a '0\r\n' last-chunk terminator returns
    DECHUNK_NO_CHUNK_TERMINATOR instead of silently being accepted.
  - Chunk extensions ('5;foo=bar') are still preserved — the ';'
    delimiter is stripped from the size line, not from the framing.

ParseSeedListBody is a 1:1 extraction of the old loop. Same behavior
on every input. Trims inline '#' comments, splits on whitespace /
comma / semicolon, normalizes CR-only line endings.

IsValidSocksNegotiationTimeout is the central policy: 5000..180000 ms
inclusive. Replaces the inline 'nTorTimeout >= 5000 && nTorTimeout <=
180000' check in init.cpp's AppInit2. Out-of-range values now emit an
InitWarning so the operator sees why their setting was ignored.

Six distinct failure-mode log messages in ThreadHTTPSeedFetch2:

  1. 'cannot connect to %s through Tor proxy'        — connect failure
  2. 'malformed response (no header terminator)'      — no \r\n\r\n
  3. 'malformed chunked transfer encoding (%s)'       — DechunkResult enum
                                                        reason string
  4. 'empty response from %s'                         — 0 bytes read
  5. 'parsed response contained zero valid addresses' — body parsed
                                                        but CService
                                                        validation
                                                        dropped all
  6. '%d addresses found from HTTPS seed list'        — success path

Help text for -torconnecttimeout now precisely describes what the
value bounds (the SOCKS5 handshake — send/recv of init/auth/connect),
not 'time to reach the onion' which was misleading. The onion-resolution
time is bounded by Tor's own SocksTimeout (~120s) and is not directly
controllable from the daemon.

src/test/http_seed_tests.cpp adds 43 new Boost.Test cases covering
every scenario in the hardening brief:

  DechunkTransferEncoding: 16 cases
    - single chunk, multiple chunks, chunk extensions (one and
      multiple), uppercase hex, payload containing CRLF, awkward
      boundary that looks like a chunk-size line, last-chunk with
      extension
    - empty body, no CRLF after size, invalid hex, empty size line,
      oversize chunk, truncated last-chunk marker, missing data CRLF,
      strtoul overflow, sign in size, whitespace in size, no last
      chunk

  ParseSeedListBody: 14 cases
    - empty, single-per-line, CRLF endings, multiple-per-line
      (space, comma, semicolon, mixed), inline comments, blank lines,
      all-comments, portless onion, invalid entry preserved, trailing
      whitespace, mixed CRLF/LF

  IsValidSocksNegotiationTimeout: 9 cases
    - 4999 (out), 5000 (in, exact lower), 60000 (in, default), 180000
      (in, exact upper), 180001 (out), 0 (out), -1 (out), INT_MAX
      (out, guard against wraparound), 3 midrange values

  Integration: 1 round-trip case
    - Encode a seed body as chunked, dechunk it, then parse the
      result. Verifies the two helpers compose correctly.

Test results: 183 test cases total, *** No errors detected. Existing
onion_v3_tests (8) and netbase_tests (10) still pass.
v5.9.23
2026-06-22 00:55:51 -07:00
Sami Ahmed 43db0138c6 merge: Tor/HTTP resilience fixes (v5.9.22)
Brings v5.9.22 to master for CI build and distribution.
- -torconnecttimeout config (5-180s, default 60s)
- Chunked-encoding aware HTTP seed body parser
- Tolerant seed parser (whitespace, commas, semicolons, comments)

3 bugs in original Claude diff fixed before merge:
- Removed orphan code referencing undefined parsed/addrStr
- Replaced non-existent AddSeed() with CService service(addr,port)
- Correct addrman.Add signature: CAddress + CService
v5.9.22
2026-06-21 19:49:15 -07:00
Sami Ahmed 78256e65d7 net: 3 Tor/HTTP resilience fixes from experimental patch
1. -torconnecttimeout config option (init.cpp, netbase.h, netbase.cpp)
   SOCKS5/Tor negotiation bound. Default 60s. Range 5-180s. Without this, a
   dead/slow .onion blocks the connecting thread (holding an outbound slot)
   until Tor's own ~120s SocksTimeout fires, starving a from-zero node.

   Implementation: SO_RCVTIMEO + SO_SNDTIMEO on the SOCKS5 socket only,
   inside Socks5(). Both Linux/BSD and Win32 paths. Configurable because
   consensus-validating nodes may want a longer ceiling than IBD nodes.

2. HTTP seed fetch: chunked-encoding support (net.cpp ThreadHTTPSeedFetch2)
   Some servers (Caddy, Let's Encrypt proxies) reply with
   Transfer-Encoding: chunked even on HTTP/1.1 + Connection: close. The
   previous parser read the body raw and saw hex chunk-size lines
   interleaved with addresses, fusing a chunk marker onto the first
   address and dropping the rest of the list (the 'only 1 address'
   symptom). De-chunk first when header advertises chunked, then parse.

3. Tolerant seed parser: whitespace/comma/semicolon separated, inline
   comments, multi-address-per-line (net.cpp)
   Real seed lists are often formatted for humans (multiple per line,
   inline comments) or older scripts (semicolons). The previous one-per-
   line, no-comments, no-inline parser lost any address that broke the
   strict format. Now strips inline '#' comments, splits on any of
   ' \t,;' so a single line can yield N addresses, and trims each.

Bugs caught and fixed before this commit (so the patch as-shipped is
clean):
- Removed orphan code referencing undefined 'parsed' and 'addrStr' vars
  from a copy-paste of an earlier draft
- Replaced non-existent 'AddSeed()' with direct 'CService service(...)'
  construction followed by 'addrman.Add(CAddress, CService)' (correct
  addrman.Add signature, not CNetAddr)
- Tightened 'addrman.Add' call to the actual signature: address + source
2026-06-21 19:48:48 -07:00
Sami Ahmed 55f1b03848 build: bump version to 5.9.21 — signed peer discovery + v3 onion validator
Release 5.9.21 includes:
  * Signed peer discovery (commit 9e9d17e) — periodic re-fire of
    getaddr/getseederlist when peer count drops, signed-peer bonus
    preference in syncmanager
  * scripts/validate_onion_seeds.py — Python validator for v3 onion
    checksums with 'did you mean' suggestions
  * scripts/pre-commit — auto-validates any triangles.conf edit
  * src/test/onion_v3_tests.cpp — 8-case Boost.Test suite
  * contrib/triangles.conf.example — pre-validated starting config
  * SYNC-SECURITY-AUDIT-2026-06-21.md addendum covering the
    corrupted .onion discovery + signed-peer architecture
v5.9.21
2026-06-21 18:17:40 -07:00
Sami Ahmed 21ab4bb4c3 contrib: add triangles.conf.example with all 7 hardcoded seeds
A canonical starting point for new operators. Pre-validated against
the v3 onion checksum, so anyone copying this file gets a known-good
config out of the box. Documents:

  * The 7 hardcoded seeds from src/onionseed.h (with port 24112)
  * How to add the 7 dynamic seeds from seeds.cryptographic-triangles.org
    (commented out, since the daemon fetches them automatically)
  * The pre-commit hook installation instructions
  * The Tor-only requirement (notor=0 must stay)
  * Standard index flags (txindex, addressindex, spentindex, timestampindex)
  * dbcache sizing guidance

The 7 hardcoded seeds were taken verbatim from src/onionseed.h and
verified by scripts/validate_onion_seeds.py. The C++ test suite
src/test/onion_v3_tests.cpp also re-validates them at every build.

Bonus: this file gets auto-validated by the pre-commit hook on every
commit, so any future edit that introduces a corrupt .onion will be
caught before it can reach a deployment.
2026-06-21 16:58:32 -07:00
Sami Ahmed fe61e34da6 docs: addendum to SYNC-SECURITY-AUDIT covering corruption + signed peers
Adds Finding 8 (corrupted v3 .onion address in test config) and
Finding 9 (signed peer discovery) to the security audit. Documents
the full chain:

  4,842 Tor 'No more HSDir' errors
    → identified as bad .onion (btb6 vs gtb6)
    → root-caused to one-character config typo
    → fixed in triangles.conf
    → built validator tool (scripts/validate_onion_seeds.py)
    → built pre-commit hook (scripts/pre-commit)
    → built C++ test suite (src/test/onion_v3_tests.cpp)
    → shipped signed peer discovery (commit 9e9d17e)

Includes a defense-in-depth table showing the 4 layers of protection
now in place (Tor checksum, Python validator, C++ tests, signed peers).

Also documents 3 remaining gaps for future work:
  1. No signing on seeds.cryptographic-triangles.org seed list
  2. No audit log of when the btb6 typo was introduced
  3. getwalletaddr creates a new key per call (should use stable node identity)
2026-06-21 16:49:54 -07:00
Sami Ahmed 20bb571690 tests: add v3 onion address validator + fix Phase 1.5 build break
Adds src/test/onion_v3_tests.cpp with 8 Boost.Test cases that validate
every hardcoded seed in src/onionseed.h against the v3 hidden service
checksum algorithm (SHA3-256 of ".onion checksum" || pubkey || version).

Test cases:
  * onion_v3_valid_known_seeds - all 7 hardcoded seeds must validate
  * onion_v3_detects_transposition - catches the btb6/gtb6 bug from 2026-06-21
  * onion_v3_detects_wrong_length - too short, too long
  * onion_v3_detects_missing_suffix - .com instead of .onion
  * onion_v3_detects_invalid_base32 - chars 0,1,8,9 + uppercase rejected
  * onion_v3_detects_bad_version_byte - all-'a' body has invalid checksum
  * onion_v3_round_trip_encoding - base32 encode/decode is deterministic
  * onion_v3_audit_summary - overall summary check

The C++ validator mirrors scripts/validate_onion_seeds.py exactly so the
two implementations stay in sync. Catches corruption at CI/build time
instead of daemon runtime.

Also fixes an unrelated build break: GetPeerInflightCap() was called from
syncmanager.cpp:533 but never declared in syncmanager.h. The function
intent was 'windowSize / peerCount + 1' - inlined that here so the test
build can succeed.
2026-06-21 16:48:58 -07:00
Sami Ahmed f58d0a5a15 scripts: add pre-commit hook that auto-validates .onion addresses
The hook scans every staged file for:
  1. Filename matches: triangles.conf, *.onion
  2. Content matches: lines starting with 'addnode=' followed by a
     base32-encoded .onion address

If any address fails v3 onion checksum validation, the commit is blocked
with a clear diagnostic showing the bad address, the reason, and (when
possible) a suggestion of the correct address.

Run with --ci mode on the validator so it exits 1 on any failure.

Install:
  cp scripts/pre-commit .git/hooks/pre-commit
  chmod +x .git/hooks/pre-commit

Bypass (NEVER do this for normal commits):
  git commit --no-verify

Tested:
  ✓ Clean config: commit allowed, validator says PASSED
  ✓ Corrupted config (btb6 vs gtb6): commit blocked with full
    diagnostic + 'did you mean: gtb6?' suggestion
2026-06-21 16:41:14 -07:00
Sami Ahmed 2bc69cd9e3 scripts: add v3 onion address validator for triangles.conf
Detects corrupted .onion addresses by validating the v3 hidden service
checksum (SHA3-256 of ".onion checksum" || pubkey || version).

Background: 2026-06-21 from-zero sync test produced 4,842 Tor
"No more HSDir" errors and 181 "ed25519 validation failed" warnings.
Root cause: a 1-character transposition (btb6 vs gtb6) in the test
config's vmepp seed address. This tool would have caught it in 0.1s.

Usage:
  ./scripts/validate_onion_seeds.py /root/.triangles/triangles.conf
  ./scripts/validate_onion_seeds.py /path/to/triangles.conf --ci
  ./scripts/validate_onion_seeds.py /path/to/triangles.conf \
    --against /root/triangles_v5/src/onionseed.h

Features:
  * Validates every addnode= line against v3 onion checksum
  * Suggests the correct address if 1-2 char transposition detected
  * Detects truncated/extended/non-base32 addresses
  * Cross-checks multiple configs (catches test vs prod mismatches)
  * CI mode exits 1 on any failure (gates deploys)
  * Pure stdlib, no pip deps (works in any Python 3.8+ env)
2026-06-21 16:36:48 -07:00
Sami Ahmed 9e9d17e1e0 sync: signed peer discovery — re-fire getaddr/getseederlist when peer count drops
Triangles already has a node-identity signing system (getwalletaddr/walletaddr
in onion_v3.cpp:4793-4848) that lets peers cryptographically prove they own
their .onion address. The problem: that handshake only fires at startup, so
a long-running sync daemon that takes 12+ hours to bootstrap gets exactly ONE
discovery round at minute 0 — and then never asks again.

This commit wires the existing signing + discovery machinery into the main
peer-connection loop, not just startup:

  * src/net.h: add nLastGetaddrTrigger + nSignedPeerBonus fields to CNode
  * src/net.cpp: in ThreadOpenConnections2, when connected onion peers < 4
    AND 5min cooldown elapsed, re-fire getaddr + getseederlist on every
    connected .onion peer. getwalletaddr is left alone (it generates a new
    receiving key per call; signed peers are cached 24h anyway).
  * src/tor/onion_v3.cpp: when HandleWalletAddrResponse verifies a peer's
    signature, set nSignedPeerBonus=1 so sync peer selection prefers them.
  * src/syncmanager.cpp: signed-peer bonus used as tiebreaker in peer sort
    (after reliability score, before blocks-delivered).

Why this matters: real-world from-zero sync of the Triangles chain took
~18 hours because only 2-3 of the 14 seed .onion nodes were reliably
reachable from any given Tor instance. With periodic re-discovery, the
daemon now has a chance to find the 12 others when the 2-3 drop.

Verified: built clean (15:59), test daemon climbed from 70,828 → 73,997+
at ~1.9 blk/s with new binary, SYNC-SIGN message confirmed firing.
2026-06-21 16:35:27 -07:00
Krystie (TRI packaging) 7de1595647 ci: fix WinGet PR creation (use 'owner:branch' not 'owner/repo:branch') 2026-06-21 02:16:41 -07:00
Krystie (TRI packaging) 758c22e5b2 ci: use unique branch per run for WinGet (triangles-VERSION-RUN#)
Avoid 'fetch first' errors when the same version gets re-distributed
(multiple tags or workflow re-runs). Each run uses its own branch in
the winget-pkgs fork.
2026-06-21 02:14:05 -07:00
Krystie (TRI packaging) b58bb2ce5f ci: fix WinGet gh pr create auth (set GH_TOKEN) 2026-06-21 02:10:55 -07:00
Krystie (TRI packaging) a548aad96c ci: fix distribute.yml chocolatey + winget step bugs
- Chocolatey 'Check' step: add shell: bash so the [ -z ] syntax parses
- WinGet fork: remove --fork flag (renamed), use --remote=false instead
  which omits the clone in the same step
2026-06-21 02:08:38 -07:00
Krystie (TRI packaging) 17b5119d40 packaging + ci: add Chocolatey auto-push + WinGet auto-PR jobs
distribute.yml:
- New 'chocolatey' job: updates nuspec version + install script SHA256,
  packs .nupkg, pushes to chocolatey.org. Gated by CHOCO_SKIP_WACATAC
  env var so it can be disabled while the Microsoft false-positive is
  still active (set CHOCO_SKIP_WACATAC=true on the repo, flip to empty
  after Microsoft clears the detection).
- New 'winget' job: forks microsoft/winget-pkgs (auto-creates fork if
  needed), generates the three manifest files (version/locale/installer)
  in the winget-pkgs v1.6.0 format, opens a PR.

Both jobs use the Windows setup.exe as the installer source.
Both jobs skip gracefully with a warning if their respective GitHub
secrets aren't set.

packaging/chocolatey/tools/chocolateyInstall.ps1:
- Rewritten to use the NSIS installer (.exe) instead of the old .zip
  format (the v5.9.x release ships an NSIS .exe setup)
- Uses $env:ChocolateyPackageVersion so the workflow can substitute the
  version at pack time
- checksum64 is '__CHECKSUM_PLACEHOLDER__' which the workflow replaces
  with the computed SHA256

Required GitHub secrets (all added):
  CHOCO_API_KEY  - Chocolatey API key
  WINGET_TOKEN   - GitHub PAT with public_repo scope
2026-06-21 02:04:59 -07:00
Krystie (TRI packaging) 794b840cdc ci: fix redacted HOMEBREW_GITHUB_TOKEN env value
The previous commit had a literal '***' placeholder where the GitHub
Actions expression ${{ secrets.HOMEBREW_GITHUB_TOKEN }} should have
been. The workflow couldn't parse, so runs showed as 'failure' with
zero jobs and the display name fell back to the file path.

Fixed by writing the correct expression directly.
2026-06-21 01:48:00 -07:00
Krystie (TRI packaging) 7213dddcf1 ci: add job-level guards to distribute.yml
Observed the workflow firing on regular push-to-master events, not just
tag pushes. GitHub is sometimes over-eager about workflow re-runs on
commits that touch the workflow file. Add an explicit job-level guard

  if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')

to all four jobs so the distribute jobs only run on tag pushes or
manual workflow_dispatch events.
2026-06-21 01:43:44 -07:00
Krystie (TRI packaging) 8b147317d5 ci: auto-distribute releases to Homebrew tap on tag
New 'homebrew' job in distribute.yml:
- Waits for the macOS .dmg to be available on the GitHub release
- Computes the new SHA256
- Clones SamiAhmed7777/homebrew-triangles
- Updates version + sha256 in both Formula/triangles.rb and
  Casks/cryptographic-triangles.rb
- Commits and pushes to main
- Skips gracefully with a warning if HOMEBREW_GITHUB_TOKEN is not set

Required GitHub secret: HOMEBREW_GITHUB_TOKEN (added)
2026-06-21 01:39:45 -07:00
Krystie (TRI packaging) 2abb72ed0e ci: fix distribute.yml to handle missing secrets per step
GitHub Actions doesn't allow 'secrets' context in 'if:' conditionals,
only in 'env:'. Reworked the workflow to:

- Capture DOCKERHUB_TOKEN and AUR_SSH_KEY into env vars at job level
- Each step that needs a secret checks env.* and exits 0 with a
  ::warning:: annotation if not set
- Skipped steps display a final summary in the job log

Same behavior, just no parser errors.
2026-06-21 01:33:23 -07:00
Krystie (TRI packaging) 06fea513d8 ci: auto-distribute releases to Docker Hub + AUR on tag
New workflow .github/workflows/distribute.yml:
- Triggers on v* tag push (and workflow_dispatch for manual runs)
- Docker job: builds + pushes to samiahmed7777/trianglesd with both
  :VERSION and :latest tags, plus a post-push smoke test
- AUR job: runs in archlinux container, downloads the release .debs,
  updates PKGBUILD with new version + SHA256s, regenerates .SRCINFO
  via makepkg, commits and pushes to AUR via SSH
- Both jobs skip gracefully (with a clear warning) if their respective
  GitHub secrets aren't set, so the workflow can be merged and tested
  before secrets are configured
- Waits up to 10 minutes for the build-all release artifacts to be
  available (build-all and distribute run in parallel on the same tag)

Required GitHub secrets:
  DOCKERHUB_TOKEN — Docker Hub access token (have in vault)
  AUR_SSH_KEY     — Private key of the AUR packager (~/.ssh/aur_key)
2026-06-21 01:30:32 -07:00
Krystie (TRI packaging) 3ddf6536e5 packaging: bump Docker + AUR to v5.9.20
Docker:
- Dockerfile now extracts from cryptographic-triangles-daemon_5.9.20_amd64.deb
  (release no longer ships raw linux-x64 binaries)
- Multi-stage build with .deb extraction
- Includes triangles-cli alongside trianglesd
- LD_LIBRARY_PATH wrapper for the bundled lib/ dir

AUR:
- Bump triangles-qt-bin to 5.9.20
- Switch from raw linux-x64 binary download (no longer published) to
  extracting the official .deb packages
- Bundle version-pinned libs in /opt/triangles/lib
- Add triangles-cli to provides
2026-06-21 01:19:09 -07:00
Sami Ahmed adbbad3121 Merge sync-freeze-fix: resolves IBD freeze at 15k + PoS header rejection at 1026
From-zero sync test confirmed: chain advances past 15k freeze zone
to 17k+ with no stall. Build clean (149/149 Ninja targets).
132/132 unit tests pass.
2026-06-20 20:56:57 -07:00
Sami Ahmed 7ba8d8b8c9 Fix sync-freeze: backpressure, prune protection, eviction direction, bridge-repair + PoS header guard
Sync-freeze patch (original):
- Backpressure ceiling HEADER_FRONT_MAX_AHEAD=8000
- PruneHeaders protects live sync window (nProtectFloor)
- Hard-cap eviction from highest-height first
- Bridge-repair getheaders from connected tip via PathReachesChain

Additional fix:
- Skip PoW check on PoS headers (nonce=0) in AddHeaderNode
  Block 1026 is PoS but within the 0-9000 PoW range — old code
  rejected valid PoS headers and severed the chain at height 1025

Verified: from-zero no-snapshot sync reached block 17k+ past the
old 15k freeze zone. 132/132 unit tests pass.
2026-06-20 20:56:47 -07:00
Sami Ahmed 6b49dd9e62 Remove legacy bootstrap.tar.gz fallback path (v2 snapshot is now the only sync)
FastImport removal in commit bdb7253 made the v2 UTXO snapshot the
canonical sync start. The legacy DownloadBootstrap() function still
attempted to fetch /triangles-bootstrap.tar.gz first, then fell back to
filelist.txt — which still contained tri-bootstrap.tar.gz. Both legacy
URLs return 404 (cleaned up 2026-06-19), so the wallet wasted a request
on a dead path before reaching the v2 snapshot URL.

Changes:
- DownloadBootstrap() no longer tries /triangles-bootstrap.tar.gz.
- Goes straight to filelist.txt → downloads the URL listed there (now
  utxo-snapshot.bin only, after the bootstrap server fix).
- Removed unused ExtractTarGz() helper function (~110 lines).
- Kept DEFAULT_HOST in bootstrap.h — init.cpp still references it
  for the SnapshotNet P2P fetch.

No version bump. v5.9.20 binary built locally; SHA
ad34764e28fb0c922a3f3570e830ba5707fdc2f7f7a11301e8c0f60356048fd3.

Bootstrap server fix landed first:
- /var/www/triangles-bootstrap/filelist.txt now contains only
  'utxo-snapshot.bin' (was tri-bootstrap.tar.gz + triangles-bootstrap.tar.gz).
This means existing laptop wallets (no rebuild needed) will now read the
updated filelist.txt on next bootstrap attempt and go straight to the
v2 snapshot URL.
2026-06-20 04:07:07 -07:00
Sami Ahmed bdb7253399 Remove -allowfastimport (FastImport) entirely
FastImport was the legacy path for rebuilding the block index from a
local blk0001.dat. With v2 UTXO snapshots now containing embedded
blocks, FastImport is redundant and dangerous (could silently index
a forked chain from a stale blk0001.dat).

Changes:
- src/main.cpp: delete FastImportBlockFile() function (~270 lines)
- src/main.h:   delete FastImportBlockFile() declaration
- src/init.cpp:  delete -allowfastimport flag handler block
                 remove from help text
                 clean up stale comments referencing FastImportBlockFile
- src/bootstrap.cpp: update stale comments

v2 snapshot loading (auto-download from bootstrap or local placement
of utxo-snapshot.bin + manifest) is now the only supported sync start.

Tested: daemon builds, runs, chain state preserved across restart.
Binary SHA: 3f26f6202947a8dc0f7933314829702aafa1e42c968368ab7ec043d57baa9519
DNS2 + DNS3 running this build, both on correct chain.

Not bumped to v5.9.21 per Sami's preference. Next formal release
will inherit this change.
v5.9.20
2026-06-19 23:56:14 -07:00
Sami Ahmed d81a36f875 Add tri CLI wrapper: wallet + secure messaging for agents and humans
A bash command interface to trianglesd RPC designed for Hermes, Krystie,
and Sami to manage TRI wallets and communicate via the built-in secure
messaging system (smessage).

Features:
- Info: status, balance, peers, staking info
- Wallet: addresses, send, transactions
- Secure messaging: inbox, outbox, send (encrypted via ECDH over Tor P2P)
- Raw RPC passthrough for any daemon command
- Bash + zsh completion
- SSH-tunneled RPC for remote node access
- Config at /etc/tri/nodes.conf (shared between agents)

Files:
- scripts/tri/tri                    Main script
- scripts/tri/nodes.conf.example     Config template
- scripts/tri/tri-completion.bash    Bash completion
- scripts/tri/_tri_zsh_completion    Zsh completion
- scripts/tri/README.md              Documentation

Tested against live DNS3 node (block 2,207,455, 4 peers).
Secure messaging verified: send → inbox → outbox all working.
2026-06-19 21:09:33 -07:00
Sami Ahmed f4f9c3b45a Merge cpp20-modernization into master: triangles-cli + macOS/Windows build fixes
Brings in from cpp20-modernization branch:
- 8aeb513: triangles-cli JSON-RPC client (bitcoin-cli pattern)
- 1d938d5: macOS build - use std::filesystem, drop Boost::system
- 600b1cf: macOS build - Boost::boost target for headers
- 569b541: Simplify DLL packaging
- 274aafa/91d9233: Windows packaging fixes
- 8c74f4e/ad26786: Packaging scripts (package-windows-daemon.sh, package-linux-daemon.sh)
2026-06-19 20:49:33 -07:00
Sami Ahmed 73c3cef8d4 bump: version 5.9.20 2026-06-19 20:19:43 -07:00
hermes a38bfd2f97 fix: auto-download UTXO snapshot when chain DB missing (3 root causes)
Three bugs prevented the wallet from automatically downloading the UTXO
snapshot when starting with stale blk0001.dat but no chain database:

1. NeedsBootstrap() only checked for blk0001.dat existence, not the chain
   DB. If blk0001.dat was present (leftover from old version) but
   txleveldb/chainstate was missing, it reported "no bootstrap needed"
   and the snapshot download never triggered.

   Fix: check for txleveldb/ or blocks/chainstate/ instead.

2. Bootstrap HTTP download was skipped when snapshotMode was true (the
   default). The code deferred to P2P snapshot fetch (Step 11.6), but
   that runs AFTER Step 7 which errored out on the FastImport gate.

   Fix: always attempt HTTP bootstrap when NeedsBootstrap is true,
   regardless of snapshotMode. The UTXO snapshot HTTP download IS the
   fast path — no reason to defer to P2P when HTTP is available.

3. FastImport gate (Step 7) was a hard InitError that killed the daemon
   before it ever reached the snapshot fetch path. blk0001.dat present
   + no chain index + FastImport disabled = immediate crash.

   Fix: instead of erroring, remove the stale blk0001.dat and continue.
   The daemon syncs from the snapshot that was already loaded in Step 6b,
   or from P2P if that somehow failed.
2026-06-19 19:40:40 -07:00
Sami Ahmed 23e8a2d647 utxosnapshot: v2 format — embed full blk0001.dat into snapshot
Per Sami's vision: 'I want to carry over the whole block inside the
UTXO.' The snapshot is now self-contained: a fresh node loading it
has everything needed (headers + UTXOs + all block bodies) without
needing a separate bootstrap tarball.

Format change (UTXO_SNAPSHOT_VERSION 1 → 2):

v1 HEADER (88 bytes):
  magic, version, network, height, blockHash, moneySupply,
  numHeaders, numUtxos, contentHash

v2 HEADER (92 bytes):
  same + numBlocks (between numUtxos and contentHash)

v2 CONTENT (after v1's headers + utxos sections):
  blocks[numBlocks]  ← raw blk0001.dat bytes, SHA256 included

DumpSnapshot changes:
- Walks ALL blocks from pindexBest to pindexGenesisBlock (was: last
  N=2000). The nHeaders arg is honored only when caller passes a
  count smaller than the full chain for v1-compat diagnostic snapshots.
- After headers + utxos sections, streams GetDataDir()/blk0001.dat
  bytes into the snapshot, chunked (64 KB), content-hashed.
- Header now writes numBlocks between numUtxos and contentHash.

LoadSnapshot changes:
- Reads numBlocks after numUtxos when version >= 2.
- After UTXOs section, streams numBlocks bytes from the snapshot
  into dataDir/blk0001.dat (uses GetDataDir() since the param dataDir
  is intentionally unnamed in this function).
- v1 snapshots still load via the partial-load path (no numBlocks in
  header, no blk0001.dat written).
- Empty snapshot check loosened to (numHeaders && numUtxos && numBlocks)
  — all three must be zero to be considered empty.

Total v2 snapshot size: ~1.9 GB (headers + blocks + UTXOs).
Generation on the operator machine: a few minutes. Download on
reasonable connection: a few minutes.

This supersedes the earlier v2 attempt (commit 69529ea) which had
compile bugs from using an unnamed dataDir parameter and had wrong
snapshot file layout.
2026-06-19 04:22:07 -07:00
Sami Ahmed d73f6015a9 Merge feature/utxo-snapshot-auto-rebuild: signature auth + auto-rebuild + crash fixes
Adds:
- bootstrap: read manifest.json + verify file SHA256 (defense in depth)
- bootstrap: signature-based snapshot authentication (replaces checkpoint gate)
- checkpoints: drop 2207680 entry (signature is the gate now)
- init: auto-rebuild trigger (-autorerebuild=N) — wipe chain DB if stale
- init: remove FastImport as primary path (-allowfastimport, default off)
- utxosnapshot: set fSerializeChainTrust=true before LoadSnapshot writes
- init: skip block verification for snapshot-sourced chains
- init: don't fail on ResetSyncCheckpoint for snapshot-sourced chains
- build: ignore build-*/ directories

Server-side: utxo-snapshot.bin symlinked to utxo-snapshot-2207680.utx on bootstrap.cryptographic-triangles.org

End-to-end verified from zero: snapshot loads to height 2207680,
bestblockhash matches manifest, 4 peers connected via Tor.

Closes PR #8. Combines all the separate branches per Sami's directive.
v5.9.19
2026-06-19 03:44:48 -07:00
Sami Ahmed ca16abe155 Merge v5.9.17-local-snapshot-trust: signed UTXO snapshot infrastructure
Adds the foundation for the snapshot-based IBD:
- sign-snapshot.sh: operator-side script to sign canonical snapshots
- utxosnapshot gate requireCheckpoint on trust source
- utxosnapshot build address index when loading (wallet balance support)
- main build address index during FastImport
- build: ignore build-*/ directories
2026-06-19 03:44:48 -07:00
Sami Ahmed be865c5944 Revert "utxosnapshot: v2 format — embed full blk0001.dat into snapshot"
This reverts commit 69529ea4c7.
2026-06-19 03:33:05 -07:00
Sami Ahmed 69529ea4c7 utxosnapshot: v2 format — embed full blk0001.dat into snapshot
Per Sami's vision: 'I want to carry over the whole block inside the
UTXO.' The snapshot should be self-contained so a fresh node is fully
usable — can serve blocks to peers, fully verify the chain, validate
txs, and resume syncing forward. Replaces the legacy tri-bootstrap.tar.gz.

Format change (UTXO_SNAPSHOT_VERSION 1 → 2):

v1 HEADER:
  magic, version, network, height, blockHash, moneySupply,
  numHeaders, numUtxos, contentHash (88 bytes)

v2 HEADER:
  same + numBlocks (92 bytes)  ← new field

v2 CONTENT (after v1's headers + utxos sections):
  blocks[numBlocks]  ← raw blk0001.dat bytes, SHA256 included

DumpSnapshot changes:
- Walks ALL blocks from pindexBest to pindexGenesisBlock (was: last
  N=2000). The nHeaders arg is honored only when 0 < nHeaders < chain
  height for v1-compat diagnostic snapshots.
- After writing headers + utxos sections, streams GetDataDir()/blk0001.dat
  bytes into the snapshot, chunked (64 KB), content-hashed.
- Header now writes numBlocks between numUtxos and contentHash.

LoadSnapshot changes:
- Reads numBlocks after numUtxos when version >= 2.
- After the UTXOs section, streams numBlocks bytes from the snapshot
  into dataDir/blk0001.dat.
- v1 snapshots (no numBlocks in header) still load via the partial
  path: headers + UTXOs only, no blk0001.dat written. The 'block
  verification skipped for snapshot-sourced chains' hack stays
  for v1, becomes unnecessary for v2.

Total v2 snapshot size: ~1.9 GB (550 MB headers + 1.3 GB blocks + 50 MB UTXOs).
Generation on the operator machine: a few minutes. Download on
reasonable connection: a few minutes.

This commit is format-only — signature verification, auto-rebuild,
and the LoadBlockIndex crash fix from PR #8 still apply unchanged.
2026-06-19 03:11:00 -07:00
Sami Ahmed dcfb650d9f init: don't fail on ResetSyncCheckpoint for snapshot-sourced chains
When LoadBlockIndex tries to reset the sync-checkpoint, it looks for
one of the known checkpoint blocks in mapBlockIndex and writes it to
the DB. For a freshly snapshot-loaded chain, mapBlockIndex only has
~1166 headers near the tip — none of the known sync checkpoints
(2205000, 2206004) are in that subset.

The reset returns false (no checkpoint found in main chain), and the
caller currently treats this as fatal: 'failed to reset sync-checkpoint'.
But for snapshot-sourced chains this is expected — the sync checkpoint
will be set when the node syncs past the next known checkpoint height.

Soften the failure: if fLoadedFromSnapshot is true, log a warning and
continue instead of erroring out.
2026-06-19 02:54:58 -07:00
Sami Ahmed 800f508abd init: skip block verification for snapshot-sourced chains
After LoadSnapshot, the daemon has headers + UTXOs but the raw block
bodies haven't been downloaded yet — they'll arrive via P2P as the
node syncs past the snapshot tip. LoadBlockIndex's verification
loop tries to read the last 50 block bodies from disk and fails
with 'OpenBlockFile failed' because the data isn't on disk yet.

Add fLoadedFromSnapshot global, set true at the end of successful
LoadSnapshot. In both txdb-leveldb.cpp and txdb-rocksdb.cpp LoadBlockIndex
verification loops, when ReadFromDisk fails AND fLoadedFromSnapshot is
true, log a warning and continue (the UTXO set itself was already
content-hash verified during LoadSnapshot, so we have strong evidence
the chain state is correct).

For non-snapshot chains (full blk0001.dat downloaded, normal IBD), the
ReadFromDisk failure remains a fatal error as before.

Combined with the prior fix in utxosnapshot.cpp that sets
fSerializeChainTrust=true before writes, the full snapshot path now
works end-to-end on a fresh datadir.
2026-06-19 02:47:30 -07:00
Sami Ahmed 78dae9fdaa utxosnapshot: set fSerializeChainTrust=true before LoadSnapshot writes
THE BUG: CDiskBlockIndex serialization is gated by a static flag
fSerializeChainTrust. LoadBlockIndex later sets this flag to true
based on dbformat >= 2 and tries to read nChainTrust as part of every
CDiskBlockIndex record.

But LoadSnapshot runs FIRST and writes CDiskBlockIndex records while
the static is still at its default value (false). The records are
written WITHOUT nChainTrust. Then LoadBlockIndex reads with flag=true,
expects nChainTrust, runs off the end of the buffer → 'CDataStream::read():
end of data: iostream error' → AppInit() exception.

This bug affected every fresh snapshot load: the snapshot's headers
and UTXOs loaded correctly (the per-record writes work), then the
post-load LoadBlockIndex crashed. Sami identified this as the
'format mismatch' blocker; the signature verification work went in
first but the underlying serialization bug remained.

Fix: explicitly set fSerializeChainTrust=true at the top of LoadSnapshot
before any CDiskBlockIndex writes. Then writes include nChainTrust.
Then LoadBlockIndex reads with the same flag set → matches.

The snapshot FILE format itself is unchanged — old snapshots produced
by daemons that wrote with flag=false will still fail to load (their
records don't have nChainTrust). New snapshots produced by daemons
that always write with flag=true (i.e. always include nChainTrust)
will load cleanly.
2026-06-19 02:42:09 -07:00
Sami Ahmed 48cf7277dd init: auto-rebuild trigger + remove FastImport as primary path
Two operational changes that together fulfill the 'snapshot as
universal sync start' vision:

1. -autorerebuild=<n> CLI flag (default 0=disabled)
   After Step 7 loads the chain DB, MaybeAutoRebuild() compares our
   local nBestHeight to the median peer-reported height (collected via
   CNode::nStartingHeight from the version handshake). If lag >= n,
   wipe the chain DB (preserve wallet.dat, onion, smsg state) and
   request shutdown. On restart, the daemon sees no chain DB and the
   snapshot path takes over.

   WaitForPeerHeights() polls up to 60s for at least 3 peers.

2. -allowfastimport CLI flag (default OFF)
   The FastImportBlockFile() rebuild path is now gated behind this
   flag. If the chain DB is empty and blk0001.dat exists, the daemon
   fails with a clear error message that tells the operator how to
   recover (place utxo-snapshot.bin, delete blk0001.dat, or set
   -allowfastimport). FastImport is now operator opt-in only — the
   snapshot path is the canonical sync start.

   This matches Sami's vision: 'Everything should be transferred over
   to the UTXO jump and then they should be able to put the blockchain
   together exactly how it's supposed to be from all the peers
   filling in all the blank spots.'
2026-06-19 02:36:09 -07:00
Sami Ahmed d6b47b5a0d checkpoints: drop 2207680 entry (signature is the snapshot gate now)
When I added the 2207680 checkpoint, I was treating checkpoints as the
authentication gate for snapshot loading. Sami corrected: 'It shouldn't
require a checkpoint, all it should require is a signature.'

Commit 2866a94 already replaced requireCheckpoint=true with signature
verification in DownloadUtxoSnapshot. This commit removes the now-
unnecessary checkpoint entry so the source stays clean — the signature
is the only gate for snapshots, period.

(2205000/2206004 checkpoints remain — they're separate concerns for
chain finality validation, not snapshot acceptance.)
2026-06-19 02:28:23 -07:00
Sami Ahmed 2866a94be1 bootstrap: signature-based snapshot authentication
DownloadUtxoSnapshot now authenticates snapshots via Triangles signed
messages instead of relying on hardcoded checkpoints.

New flow:
1. Fetch big manifest.json, find canonical snapshot entry
2. Fetch the per-snapshot manifest (utxo-snapshot-{h}.manifest.json)
3. Verify the signer address is in the trusted signers list (currently
   Sami's TG8f76yktTxDrT7JJymY3wVAusXiD3fVvX)
4. Verify the signature cryptographically (Triangles compact-message
   protocol with strMessageMagic prefix, same construction as
   signmessage/verifymessage RPC)
5. Download snapshot file, verify SHA256 against manifest
6. Load with requireCheckpoint=false — signature is the gate

Per Sami: 'It shouldn't require a checkpoint all it should require
is a signature.' This removes the checkpoint coupling that was
breaking fresh-node sync (the 2207680 checkpoint gate rejected the
canonical snapshot even though it was validly signed).

Trusted signer list is currently a hardcoded constant. Future work:
-snapshotsigner=<addr> CLI arg (repeatable).
2026-06-19 02:24:07 -07:00
Sami Ahmed 2a7c89a91e bootstrap: read manifest.json for canonical snapshot + verify SHA256
DownloadUtxoSnapshot now:
1. Fetches manifest.json from the bootstrap server
2. Locates the utxo_snapshot entry (filename + expected sha256)
3. Downloads THAT file
4. Verifies file SHA256 matches manifest
5. Falls back to legacy 'utxo-snapshot.bin' if manifest unavailable

Also add 2207680 checkpoint to mapCheckpoints so the canonical signed
snapshot (per 2026-06-18 manifest) passes the requireCheckpoint gate.

Defense in depth: server symlinks + daemon verifies the file matches.
2026-06-19 02:02:53 -07:00
Sami Ahmed 677a8ea79a build: ignore build-*/ directories and build artifacts 2026-06-19 00:17:10 -07:00
SamiAhmed7777 b40c58f886 Add triangles-cli: JSON-RPC client (port bitcoin-cli pattern) (#7)
* Add triangles-cli: JSON-RPC client (port bitcoin-cli pattern)

Triangles never had a CLI client (bitcoin-cli analog). This adds
triangles-cli as a third build target alongside trianglesd and
triangles-qt.

- src/triangles-cli.cpp: self-contained JSON-RPC 1.0 client.
  Reads triangles.conf for credentials, supports -rpcuser/-rpcpassword
  /-rpcconnect/-rpcport/-testnet/-datadir/-conf flags. Implements
  -getinfo (synthesized summary from getnetworkinfo/getblockchaininfo
  /getwalletinfo) and raw method dispatch. JSON via json_spirit compat
  shim (json_compat.h), HTTP via boost::asio, base64 auth inline.
  No util.cpp / wallet.cpp / net.cpp / triangles_common link dep —
  keeps the binary small (~600 KB Linux, ~1.5 MB Windows).

- CMake: new option(BUILD_CLI ON) + add_executable(triangles-cli)
  in src/CMakeLists.txt. Status line added.

- CI: BUILD_CLI=ON added to build-windows-daemon and build-linux-daemon
  jobs. triangles-cli.exe bundled into windows-daemon artifact
  alongside trianglesd.exe. triangles-cli added to linux-daemon .deb
  package (with launcher in /usr/bin).

- Default ON; set BUILD_CLI=OFF to skip.

Closes the open 'triangles-cli.exe missing from Windows build'
follow-up (the binary wasn't missing — it never existed).

Patterned after Bitcoin Core bitcoin-cli and Dash Core dash-cli.

* Fix macOS build: drop Boost::system/find_package component, use std::filesystem

Homebrew's boost formula doesn't ship the boost_system CMake config file,
so find_package(Boost REQUIRED COMPONENTS system) failed on macOS.

- Replace boost::filesystem with std::filesystem (C++17, no Boost dep)
- Drop 'filesystem' from find_package — only headers needed (asio + system)
- Link libboost_system explicitly per-platform by library name, resolved
  via the platform's default search path (Homebrew toolchain on macOS,
  system libs on Linux, MSYS2 on Windows)

CI will rerun automatically on PR push.

* Fix macOS build: add Boost::boost target for headers, link boost_system

The previous fix dropped the find_package component but also killed the
boost include path. Now use the modern Boost::boost header-only target
(available in Boost 1.83+) which sets up include directories without
requiring a per-component config file.

Link libboost_system explicitly by name on all platforms — the linker
finds it via the platform's default search path:
- Linux: /usr/lib (libboost_system.so)
- macOS Homebrew: /opt/homebrew/lib (libboost_system.dylib)
- Windows MSYS2: mingw64/bin (libboost_system-mt-X-XX.dll)

* Drop Boost entirely from triangles-cli: use raw sockets for HTTP

Third time's the charm. After two CI failures chasing boost::asio / libboost_system
linking issues across platforms (Homebrew missing config on macOS, MSYS2 versioned
names on Windows, CMake targets that don't quite work everywhere), rip the whole
Boost dependency out of the CLI and use raw POSIX/Winsock sockets.

- triangles-cli.cpp: replaced boost::asio with raw socket() / connect() / send()
  / recv() / getaddrinfo(). Cross-platform: #ifdef _WIN32 for Winsock + WSAStartup
  / WSACleanup, else POSIX. ~100 lines of clean portable socket code.
- src/CMakeLists.txt: dropped find_package(Boost) entirely. Only links
  json_compat (header-only) + ws2_32 on Windows. No boost libs to find.

Should be the last fix needed for this PR.

* Fix Windows packaging step: simplify bash { } | sort -u | while pattern

The previous step used a bash group command piped through sort -u and a
while loop. Under MSYS2 bash + 'set -e -o pipefail' (GitHub Actions
default), this triggered a non-zero exit even when the loop body
succeeded, causing the Windows daemon job to fail at the packaging step
(the actual link of both trianglesd.exe and triangles-cli.exe succeeded).

Replaced the { } | sort -u | while pattern with a temp-file-based dedup:
- ldd both binaries, append to /tmp/cli-dlls.txt (or cli-libs.txt on Linux)
- sort -u the temp file
- pipe the result into the while loop (simpler pipeline, no group)

Also applied the same simplification to the Linux .deb packaging for
consistency, even though the Linux build was passing.

* Simplify DLL packaging: plain for loop, no pipe-into-while

The previous attempts used 'ldd | sort -u | while read; do ... done' patterns
that exit 1 under MSYS2 bash + 'set -e -o pipefail' even when the script
ran successfully. Replaced with a plain 'for bin in ...; do ldd > list.txt;
while read; do cp; done < list.txt; done' pattern that has no pipelines
other than the standard redirection, and uses IFS= read -r for safe line
iteration.

Also moved temp files from /tmp to the working directory (./dll-list.txt)
to avoid any MSYS2 /tmp path-translation edge cases.

* diagnostic: add tracing to Windows packaging step

* Add package-windows-daemon.sh + package-linux-daemon.sh scripts

Move the Windows daemon packaging step and the Linux .deb build into
committed shell scripts under scripts/ci/. This bypasses GitHub Actions'
inline-run-block quirks (silent exit 1 under msys2 + set -e -o pipefail
with multi-line scripts) and makes the packaging logic debuggable locally.

* Switch to script-file packaging for Windows + Linux daemon jobs

Replace inline multi-line run: blocks with invocations of the
scripts/ci/package-*.sh scripts. This sidesteps the GitHub Actions
msys2 + 'set -e -o pipefail' issue that caused silent exit 1 on the
Windows daemon packaging step. The scripts are also debuggable locally.

---------

Co-authored-by: Krystie <krystie@sami>
2026-06-18 20:05:54 -07:00
Krystie ad267866ab Switch to script-file packaging for Windows + Linux daemon jobs
Replace inline multi-line run: blocks with invocations of the
scripts/ci/package-*.sh scripts. This sidesteps the GitHub Actions
msys2 + 'set -e -o pipefail' issue that caused silent exit 1 on the
Windows daemon packaging step. The scripts are also debuggable locally.
2026-06-18 19:50:10 -07:00