Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ bb-vpn enroll <bb-vpn://enroll?uuid=…> # Submit an enrollment URI (the menuba
bb-vpn --version # Print version (ldflags-stamped at build time)
```

### .pkg installer (Phase 4 + 5)
### .pkg installer (Phase 4 + 5 + 6)
```bash
make build-bb-vpn-host # Host-arch bb-vpn binary -> build/bb-vpn (dev/test)
make build-bb-vpn-pkg # Darwin universal bb-vpn -> build/pkg/bb-vpn (for .pkg)
Expand All @@ -100,6 +100,19 @@ make test-bb-vpn # Run client/bb-vpn Go tests
make build-pkg # Assemble BB-VPN-<ver>.pkg (incl. BBVPN.app) in client/pkg-build/dist/
```

Phase 6 adds ad-hoc codesigning to `build.sh` (`codesign -s - --force`
on the standalone bb-vpn/sing-box/xray Mach-Os, `codesign -s - --force
--deep` on `BBVPN.app`; no Apple Developer license, no notarization —
Gatekeeper still shows "unidentified developer" on first install + first
launch) and a
user-facing install page template at
`client/pkg-build/install-page-template.html`. The operator-facing
host/distribute runbook (build, sign, host on a long-random nginx
location, per-user install page via envsubst, token rotation,
verification) lives in [`docs/release.md`](docs/release.md). Future
operators/agents touching the .pkg flow should read it before
modifying `build.sh` or the install page.

`vpn-start` no longer parses its own flags. Any args after the program name
are forwarded verbatim to `render-config`; xray-need is auto-detected from
the rendered sing-box config (presence of any `xhttp-*` SOCKS outbound).
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ openssl s_client -connect <relay-ip>:443 -servername <xhttp_sni> -alpn h2 -tls1_
# NOT xray's synthetic cert.
```

## .pkg distribution (Phase 4–6)

The macOS `.pkg` installer flow under `client/pkg-build/` (with the
`bb-vpn` control-plane binary in `client/bb-vpn/` and the `BBVPN.app`
menu-bar app in `client/menubar/`) is the supported way to ship to
end-users. Build with `make build-pkg`, host the resulting
`BB-VPN-<ver>.pkg` + per-user install page on a long-random URL, and
share that URL out-of-band.

Full operator runbook (build, ad-hoc codesign, host, per-user install
page, token rotation, verification): [docs/release.md](docs/release.md).

## Files

```
Expand All @@ -163,6 +175,13 @@ config/
sing-box.template.json - Legacy single-server sing-box template
com.xray-xhttp.plist - launchd plist for xray-core
com.sing-box-vpn.plist - launchd plist for sing-box
client/
bb-vpn/ - Go control-plane CLI (shipped in the .pkg)
menubar/ - SwiftUI BBVPN.app sources
pkg-build/ - .pkg installer assembly (build.sh, postinstall.sh, install-page-template.html)
docs/
release.md - Phase 6 operator runbook for the .pkg flow
control-plane-bootstrap.md - Phase 1 control-plane setup
scripts/
deploy.sh - First-time server deployment
xray-users - User management CLI
Expand Down
37 changes: 37 additions & 0 deletions client/pkg-build/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ extract_version() {
command -v jq >/dev/null 2>&1 || die "jq is required"
command -v pkgbuild >/dev/null 2>&1 || die "pkgbuild is required (Xcode CLT)"
command -v productbuild >/dev/null 2>&1 || die "productbuild is required (Xcode CLT)"
command -v codesign >/dev/null 2>&1 || die "codesign is required (Xcode CLT)"

EXPECT_BB=$(jq -r '.bb_vpn' "$MANIFEST")
EXPECT_SB=$(jq -r '.sing_box' "$MANIFEST")
Expand Down Expand Up @@ -148,9 +149,45 @@ jq --arg tok "$TOKEN_TRIMMED" \
> "$STAGING_DIR/Library/Application Support/bb-dpi/control-plane.json"
chmod 0600 "$STAGING_DIR/Library/Application Support/bb-dpi/control-plane.json"

# Phase 6: ad-hoc codesign every executable + the .app bundle in the
# staging tree. `-s -` is ad-hoc (no Apple identity); the signature
# only gives each binary a stable code-signing identifier so the
# kernel's library-validation and TCC paths don't trip on completely
# unsigned binaries. Gatekeeper still treats the .pkg + .app as
# "unidentified developer" — the user-facing right-click → Open dance
# is documented in docs/release.md.
#
# `--force` overwrites any existing signature (upstream sing-box and
# xray release builds ship with their own ad-hoc sigs we don't want to
# rely on). `--deep` recurses into BBVPN.app's bundle and signs the
# embedded Mach-O at Contents/MacOS/BBVPN in the same invocation, so
# no separate inner pre-sign is needed.
blue "ad-hoc codesigning payload..."
SIGN_BINS=(
"$STAGING_DIR/Library/Application Support/bb-dpi/bin/bb-vpn"
"$STAGING_DIR/Library/Application Support/bb-dpi/bin/sing-box"
"$STAGING_DIR/Library/Application Support/bb-dpi/bin/xray"
)
for bin in "${SIGN_BINS[@]}"; do
codesign --sign - --force --timestamp=none "$bin"
done
codesign --sign - --force --deep --timestamp=none \
"$STAGING_DIR/Applications/BBVPN.app"

# Verify everything we just signed actually validates.
for bin in "${SIGN_BINS[@]}"; do
codesign --verify --strict "$bin" || die "codesign verify failed: $bin"
done
codesign --verify --deep --strict \
"$STAGING_DIR/Applications/BBVPN.app" || die "codesign verify failed: BBVPN.app"
green "ad-hoc signatures verified."

# Strip xattrs so pkgbuild doesn't emit AppleDouble ._* sidecars for
# every payload file (com.apple.provenance and friends on brew-installed
# binaries). Keeps the .pkg smaller and the payload listing clean.
# Runs AFTER codesign — the signature is stored inside the Mach-O
# LC_CODE_SIGNATURE load command (and inside the .app's
# Contents/_CodeSignature/), not as an xattr, so xattr -cr is safe.
xattr -cr "$STAGING_DIR"

# postinstall lives in pkgbuild's --scripts dir, NOT the payload tree.
Expand Down
144 changes: 144 additions & 0 deletions client/pkg-build/install-page-template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<!DOCTYPE html>
<!--
install-page-template.html — user-facing install page.

Filled by the operator at distribution time via envsubst:
PKG_URL full URL of the BB-VPN-<ver>.pkg download
PKG_NAME filename only (BB-VPN-<ver>.pkg) — shown in the UI
ENROLL_URI bb-vpn://enroll?uuid=<UUID> for THIS user
USER_NAME human name for the personal greeting

Hosted on the same cover-site as the .pkg, under a long-random
path. The .pkg URL and the enrollment URI are both per-cohort
secrets — anyone with this page can both download and enroll.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>BB-VPN install — ${USER_NAME}</title>
Comment thread
fitz123 marked this conversation as resolved.
<style>
:root {
color-scheme: light dark;
--bg: #f7f7f8;
--fg: #1a1a1a;
--muted: #6b7280;
--card: #ffffff;
--border: #e5e7eb;
--accent: #2563eb;
--accent-fg: #ffffff;
--warn-bg: #fff8e1;
--warn-border: #f0c94c;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0b0b0d;
--fg: #e5e7eb;
--muted: #9ca3af;
--card: #18181b;
--border: #27272a;
--accent: #3b82f6;
--warn-bg: #2a2208;
--warn-border: #92660a;
}
}
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg);
font: 16px/1.55 -apple-system, BlinkMacSystemFont, "SF Pro Text",
"Helvetica Neue", Arial, sans-serif; }
.wrap { max-width: 720px; margin: 0 auto; padding: 32px 24px 64px; }
h1 { font-size: 26px; margin: 0 0 4px; letter-spacing: -0.01em; }
h2 { font-size: 18px; margin: 28px 0 10px; }
p { margin: 8px 0; }
.muted { color: var(--muted); font-size: 14px; }
.card { background: var(--card); border: 1px solid var(--border);
border-radius: 10px; padding: 18px 20px; margin: 16px 0; }
.btn { display: inline-block; padding: 12px 18px; border-radius: 8px;
background: var(--accent); color: var(--accent-fg); text-decoration: none;
font-weight: 600; }
.btn:hover { filter: brightness(1.05); }
ol { padding-left: 22px; }
ol li { margin: 6px 0; }
code { background: var(--bg); padding: 1px 6px; border-radius: 4px;
font: 13px/1.4 ui-monospace, "SF Mono", Menlo, monospace; }
.warn { background: var(--warn-bg); border: 1px solid var(--warn-border);
padding: 12px 16px; border-radius: 8px; font-size: 14px; }
.step-num { display: inline-block; width: 22px; height: 22px;
background: var(--accent); color: var(--accent-fg); border-radius: 50%;
text-align: center; font-size: 13px; font-weight: 700; line-height: 22px;
margin-right: 6px; vertical-align: 1px; }
hr { border: 0; border-top: 1px solid var(--border); margin: 28px 0; }
details { margin: 12px 0; }
details summary { cursor: pointer; color: var(--accent); font-size: 14px; }
</style>
</head>
<body>
<div class="wrap">

<h1>BB-VPN install</h1>
<p class="muted">Personal link for <strong>${USER_NAME}</strong>. Don't share this page — both the installer and your enrollment link are on it.</p>

<div class="card">
<p><span class="step-num">1</span><strong>Download the installer</strong></p>
<p><a class="btn" href="${PKG_URL}" download>Download ${PKG_NAME}</a></p>
<p class="muted">~20 MB. Save it somewhere you can find in Finder (Downloads is fine).</p>
Comment thread
fitz123 marked this conversation as resolved.
</div>

<div class="card">
<p><span class="step-num">2</span><strong>Right-click → Open the installer</strong></p>
<p>Find <code>${PKG_NAME}</code> in Finder. Don't double-click it.</p>
<ol>
<li>Right-click (or Control-click) the file.</li>
<li>Choose <strong>Open</strong> from the menu.</li>
<li>macOS will show a warning: <em>&ldquo;${PKG_NAME} cannot be opened because Apple cannot check it for malicious software.&rdquo;</em> Click <strong>Open</strong>.</li>
<li>The standard installer opens. Click through. Enter your Mac password when asked.</li>
</ol>
<div class="warn">
<strong>Why the warning?</strong> The installer isn't signed with an Apple Developer ID (we don't have a paid Apple Developer account). Right-click → Open is the official one-time approval flow. macOS remembers your approval; you won't see this dialog again for this file. (Text-only instructions are intentional — no screenshots are shipped with the page.)
</div>
</div>

<div class="card">
<p><span class="step-num">3</span><strong>BBVPN starts automatically</strong></p>
<p>After the installer finishes, BBVPN launches on its own — a small icon (grey or yellow circle) appears in your menu bar within a few seconds. If the icon doesn't show up, open <strong>/Applications/BBVPN.app</strong> once via right-click → Open → click <strong>Open</strong> on the warning dialog.</p>
</div>

<div class="card">
<p><span class="step-num">4</span><strong>Enroll</strong></p>
<p>Click the button below. Your browser will ask if you want to open the link in BBVPN — click <strong>Allow</strong> or <strong>Open BBVPN</strong>.</p>
<p><a class="btn" href="${ENROLL_URI}">Enroll this Mac</a></p>
<details>
Comment thread
fitz123 marked this conversation as resolved.
<summary>The button doesn't open BBVPN — what now?</summary>
<p>Open Terminal (Spotlight → &ldquo;Terminal&rdquo;) and run:</p>
<p><code>sudo "/Library/Application Support/bb-dpi/bin/bb-vpn" enroll '${ENROLL_URI}'</code></p>
</details>
</div>

<hr>

<h2>How do I know it's working?</h2>
<p>The menu bar icon turns <strong>green</strong> within a minute of enrollment. Click the icon to see your exit country and which services are running. Visit <a href="https://ifconfig.co" target="_blank" rel="noopener">ifconfig.co</a> — the IP shown should NOT be your real one.</p>

<h2>It's stuck on yellow / grey</h2>
<ol>
<li>Grey: not enrolled yet — repeat step 4.</li>
<li>Yellow with &ldquo;syncing&rdquo;: wait 15-30 seconds, the first sync is in flight.</li>
<li>Yellow with an error: click the icon, copy the error line, and DM the operator.</li>
</ol>

<h2>Stop / start</h2>
<p>In Terminal:</p>
<p><code>sudo "/Library/Application Support/bb-dpi/bin/bb-vpn" stop</code> — turn the VPN off (survives reboots).<br>
<code>sudo "/Library/Application Support/bb-dpi/bin/bb-vpn" start</code> — turn it back on.</p>
<p class="muted">The absolute path is needed because <code>sudo</code> resets <code>$PATH</code> and the <code>~/.local/bin/bb-vpn</code> shortcut isn't on its search path.</p>

<h2>Uninstall</h2>
<p>If you ever need to remove BB-VPN, in Terminal:</p>
<p><code>sudo /Library/Application\ Support/bb-dpi/bin/bb-vpn-uninstall</code></p>

<hr>
<p class="muted">Trouble? DM the operator. Don't forward this page.</p>

</div>
</body>
</html>
Loading
Loading