This commit is contained in:
Ariska committed 2026-07-08 08:08:53 +07:00
1 parent f7961b8e70
commit f8357220fc
9 files changed
+197 -51

No files matched your search

Binary file not shown.
+19 -11
View File
@@ -114,16 +114,10 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
}
function messageStatusHtml(m) {
if (m.sender !== 'me') {
return '<span class="chat-msg-status received">Received</span>';
if (m.status === 'failed') {
return '<span class="chat-msg-status pending">Failed to send</span>';
}
if (m.deliveredAt) {
return '<span class="chat-msg-status delivered">Sent · Received by contact</span>';
}
if (m.id) {
return '<span class="chat-msg-status sent">Sent</span>';
}
return '<span class="chat-msg-status pending">Sending…</span>';
return '';
}
function mergeMessages(peerId, incoming) {
@@ -312,6 +306,7 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
if (!body) return;
const senderId = selfPeerId() || 'local';
const name = selfName();
const thread = getThread(selectedPeer.peerId);
sendBtn.disabled = true;
try {
const res = await BackOneAuth.fetchWithAuth(
@@ -327,7 +322,7 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
})
}
);
if (!res.ok) return;
if (!res.ok) throw new Error(`secure chat send failed: ${res.status}`);
const data = await res.json();
const now = data.createdAt || Date.now();
const msg = {
@@ -339,12 +334,25 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
deliveredAt: null,
status: 'sent'
};
getThread(selectedPeer.peerId).push(msg);
thread.push(msg);
if (msg.at > lastPollSince) lastPollSince = msg.at;
persistThread(networkId, selectedPeer.peerId);
input.value = '';
render();
} catch (_) {
const now = Date.now();
thread.push({
id: null,
sender: 'me',
senderName: name,
body,
at: now,
deliveredAt: null,
status: 'failed'
});
if (now > lastPollSince) lastPollSince = now;
persistThread(networkId, selectedPeer.peerId);
render();
} finally {
updateInputs();
}
@@ -114,16 +114,10 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
}
function messageStatusHtml(m) {
if (m.sender !== 'me') {
return '<span class="chat-msg-status received">Received</span>';
if (m.status === 'failed') {
return '<span class="chat-msg-status pending">Failed to send</span>';
}
if (m.deliveredAt) {
return '<span class="chat-msg-status delivered">Sent · Received by contact</span>';
}
if (m.id) {
return '<span class="chat-msg-status sent">Sent</span>';
}
return '<span class="chat-msg-status pending">Sending…</span>';
return '';
}
function mergeMessages(peerId, incoming) {
@@ -313,6 +307,7 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
if (!body) return;
const senderId = selfPeerId() || 'local';
const name = selfName();
const thread = getThread(selectedPeer.peerId);
sendBtn.disabled = true;
try {
const res = await BackOneAuth.fetchWithAuth(
@@ -328,7 +323,7 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
})
}
);
if (!res.ok) return;
if (!res.ok) throw new Error(`secure chat send failed: ${res.status}`);
const data = await res.json();
const now = data.createdAt || Date.now();
const msg = {
@@ -340,12 +335,25 @@ window.BackOneAddons['secure-chat'] = function secureChatAddon() {
deliveredAt: null,
status: 'sent'
};
getThread(selectedPeer.peerId).push(msg);
thread.push(msg);
if (msg.at > lastPollSince) lastPollSince = msg.at;
persistThread(networkId, selectedPeer.peerId);
input.value = '';
render();
} catch (_) {
const now = Date.now();
thread.push({
id: null,
sender: 'me',
senderName: name,
body,
at: now,
deliveredAt: null,
status: 'failed'
});
if (now > lastPollSince) lastPollSince = now;
persistThread(networkId, selectedPeer.peerId);
render();
} finally {
updateInputs();
}
+75 -5
View File
@@ -6,11 +6,81 @@ set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BACKONE_REPO="${BACKONE_REPO:-$ROOT/../BackOne}"
if [[ "$(uname -s)" != "Linux" ]]; then
echo "This script is Linux-only." >&2
echo "On macOS, edit /Library/Application Support/BackOne/local.conf as valid JSON," >&2
echo "then restart the BackOne daemon/app instead of using systemctl." >&2
exit 1
if [[ "$(uname -s)" == "Darwin" ]]; then
CONF="/Library/Application Support/BackOne/local.conf"
PORT="$(cat "/Library/Application Support/BackOne/backone.port" 2>/dev/null || echo 9993)"
echo "macOS secure chat settings"
echo ""
if [[ "${EUID}" -eq 0 ]]; then
python3 - <<'PY'
import json
from pathlib import Path
path = Path("/Library/Application Support/BackOne/local.conf")
cfg = json.loads(path.read_text())
settings = cfg.setdefault("settings", {})
settings["webhubBind"] = "overlay"
settings["webhubAllowOverlayChat"] = True
settings.setdefault("webhubPort", 9993)
path.write_text(json.dumps(cfg, indent=2) + "\n")
print(f"Updated {path}")
print(" webhubBind = overlay")
print(" webhubAllowOverlayChat = true")
print(" webhubPort =", settings.get("webhubPort", 9993))
PY
echo ""
echo "Restarting backone daemon..."
pkill -x backone 2>/dev/null || true
sleep 1
"/Library/Application Support/BackOne/launch.sh" >/tmp/backone.log 2>&1 &
sleep 2
else
echo "Run with sudo to apply overlay settings automatically:"
echo " sudo $0"
echo ""
echo "Or verify ${CONF} contains:"
echo ' "settings": {'
echo ' "webhubBind": "overlay",'
echo ' "webhubAllowOverlayChat": true'
echo ' }'
echo ""
python3 - <<'PY' 2>/dev/null || true
import json
from pathlib import Path
path = Path("/Library/Application Support/BackOne/local.conf")
try:
cfg = json.loads(path.read_text())
settings = cfg.get("settings") or {}
print("Current settings:")
print(" webhubBind =", settings.get("webhubBind"))
print(" webhubAllowOverlayChat =", settings.get("webhubAllowOverlayChat"))
print(" webhubPort =", settings.get("webhubPort", 9993))
if settings.get("webhubBind") not in (None, "", "overlay"):
print("WARN: webhubBind should be overlay for cross-host chat on macOS")
except Exception as exc:
print(f"Could not parse {path}: {exc}")
PY
echo ""
echo "Then restart the daemon:"
echo ' sudo pkill -x backone'
echo ' sudo "/Library/Application Support/BackOne/launch.sh" >/tmp/backone.log 2>&1 &'
echo ""
fi
echo "Verifying listen address (port ${PORT})..."
if command -v lsof >/dev/null 2>&1; then
lsof -nP -iTCP -sTCP:LISTEN 2>/dev/null | rg ":${PORT} " || echo " WARN: nothing listening on port ${PORT}" >&2
fi
echo ""
echo "Verifying overlay relay (no auth) on 127.0.0.1:${PORT}..."
code="$(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' \
-d '{"networkId":"0000000000000000","senderId":"0000000000","senderName":"t","targetId":"0000000000","body":"t","at":1}' \
"http://127.0.0.1:${PORT}/controller/comms/chat/relay")"
echo " POST /controller/comms/chat/relay -> HTTP ${code}"
if [[ "$code" != "200" ]]; then
echo " WARN: relay no-auth check failed — peers may see connection errors" >&2
fi
exit 0
fi
if [[ "${EUID}" -ne 0 ]]; then
+24 -7
View File
@@ -7,12 +7,21 @@ NETWORK_ID="${1:?network id required (16 hex chars)}"
TARGET_ID="${2:?target peer id required (10 hex chars)}"
MSG="${3:-ping from send-secure-chat-test.sh}"
OS="$(uname -s)"
if [[ "$OS" == "Darwin" ]]; then
SERVICE_HOME="/Library/Application Support/BackOne"
DEFAULT_HUB_PORT=9993
else
SERVICE_HOME="/var/lib/backone"
DEFAULT_HUB_PORT=9994
fi
TOKEN="$(
cat ~/.backone-local-auth 2>/dev/null \
|| cat ~/.backOneAuthToken 2>/dev/null \
|| sudo cat /var/lib/backone/authtoken.secret
|| sudo cat "${SERVICE_HOME}/authtoken.secret"
)"
PORT="$(cat /var/lib/backone/backone.port 2>/dev/null || echo 9993)"
PORT="$(cat "${SERVICE_HOME}/backone.port" 2>/dev/null || echo 9993)"
MY_ID="$(
curl -sf -H "X-ZT1-Auth: $TOKEN" "http://127.0.0.1:${PORT}/status" \
@@ -24,8 +33,12 @@ HUB_PORT="$(
| jq -r '.webhubPort // empty' 2>/dev/null || true
)"
if [[ -z "$HUB_PORT" ]]; then
if ss -tln 2>/dev/null | grep -q '127.0.0.1:9994'; then
HUB_PORT=9994
if [[ "$OS" == "Darwin" ]]; then
if nc -z 127.0.0.1 "$DEFAULT_HUB_PORT" 2>/dev/null; then
HUB_PORT="$DEFAULT_HUB_PORT"
fi
elif ss -tln 2>/dev/null | rg -q "127\\.0\\.0\\.1:${DEFAULT_HUB_PORT}"; then
HUB_PORT="$DEFAULT_HUB_PORT"
else
echo "Could not detect webhub port. Is backone-webhub running?" >&2
exit 1
@@ -52,12 +65,12 @@ else
echo "Peer VL1: not in /peer list"
fi
if ! cmp -s /usr/sbin/backone "${BACKONE_REPO:-$HOME/git/BackOne}/backone" 2>/dev/null; then
if [[ "$OS" != "Darwin" ]] && ! cmp -s /usr/sbin/backone "${BACKONE_REPO:-$HOME/git/BackOne}/backone" 2>/dev/null; then
echo ""
echo "WARN: system backone is not the patched build — run:" >&2
echo " sudo ./scripts/install-secure-chat-deps.sh" >&2
fi
if ! cmp -s /usr/lib/backone/addons/webhub/backone-webhub \
if [[ "$OS" != "Darwin" ]] && ! cmp -s /usr/lib/backone/addons/webhub/backone-webhub \
"${BACKONE_REPO:-$HOME/git/BackOne}/addons/webhub/backone-webhub" 2>/dev/null; then
echo "WARN: system webhub is outdated — run install script above" >&2
fi
@@ -74,7 +87,11 @@ for candidate in /usr/bin/python3 python3; do
done
if [[ -z "$PYTHON" ]]; then
echo "python3-websockets is required. Install with:" >&2
echo " sudo apt install python3-websockets" >&2
if [[ "$OS" == "Darwin" ]]; then
echo " python3 -m pip install websockets" >&2
else
echo " sudo apt install python3-websockets" >&2
fi
echo " # or: pip install websockets (outside the BackOneUI venv)" >&2
exit 1
fi
+17 -5
View File
@@ -26,6 +26,10 @@ use crate::serviceclient::ServiceClient;
const WINDOW_W: c_int = 960;
const WINDOW_H: c_int = 640;
#[cfg(target_os = "macos")]
const DEFAULT_WEBHUB_PORT: u16 = 9993;
#[cfg(not(target_os = "macos"))]
const DEFAULT_WEBHUB_PORT: u16 = 9994;
const DEFAULT_BACKONE_PORT: u16 = 9993;
@@ -235,7 +239,13 @@ unsafe fn send_current_message() -> bool {
if !ports.contains(&DEFAULT_BACKONE_PORT) {
ports.push(DEFAULT_BACKONE_PORT);
}
if c.realtime_chat_available {
let overlay_contact = cv.contact.ip.starts_with("192.168.") || cv.contact.ip.starts_with("10.");
#[cfg(target_os = "macos")]
let prefer_relay = overlay_contact;
#[cfg(not(target_os = "macos"))]
let prefer_relay = false;
if c.realtime_chat_available && !prefer_relay {
let _ = c.hub_cmd.send(HubCommand::SendChatRemote {
host: target_host.clone(),
ports,
@@ -254,7 +264,7 @@ unsafe fn send_current_message() -> bool {
});
let empty = CString::new("").unwrap();
libui::uiMultilineEntrySetText(compose_entry, empty.as_ptr());
if !c.realtime_chat_available {
if !c.realtime_chat_available || prefer_relay {
let relayed = relay::relay_chat_to_peer_paths(
&c.client,
&network_id,
@@ -1274,11 +1284,13 @@ pub fn secure_chat_main() {
}
let auth_token = client.auth_token().to_string();
let hub_host = client.webhub_host();
let hub_host = client.webhub_local_host();
let hub_port = client.webhub_port();
let local_webhub_available = client.local_webhub_reachable();
let realtime_chat_available =
client.http_get_json("/controller/comms/ping?host=127.0.0.1").is_some();
let realtime_chat_available = client
.http_get_json("/controller/comms/ping?host=127.0.0.1")
.is_some()
|| (cfg!(target_os = "macos") && local_webhub_available);
let (hub_cmd, hub_events) = if realtime_chat_available {
spawn_webhub(auth_token, hub_host, hub_port)
} else {
+7 -1
View File
@@ -9,6 +9,12 @@ use super::contacts::{add_contact, SecureContact};
pub const QR_TYPE: &str = "backone-secure-chat-contact";
#[cfg(target_os = "macos")]
const DEFAULT_CONTACT_PORT: u16 = 9993;
#[cfg(not(target_os = "macos"))]
const DEFAULT_CONTACT_PORT: u16 = 9994;
pub fn contacts_qr_path() -> PathBuf {
let home = unsafe { crate::APPLICATION_HOME.as_str() };
PathBuf::from(home).join("my-secure-chat-qr.png")
@@ -76,7 +82,7 @@ pub fn parse_contact_payload(value: &Value) -> Result<SecureContact, String> {
let port = value
.get("port")
.and_then(|v| v.as_u64())
.unwrap_or(9994) as u16;
.unwrap_or(DEFAULT_CONTACT_PORT as u64) as u16;
let network_id = value
.get("networkId")
.and_then(|v| v.as_str())
+24 -11
View File
@@ -62,6 +62,28 @@ fn is_loopback_host(host: &str) -> bool {
host == "127.0.0.1" || host == "localhost" || host == "::1"
}
fn is_private_host(host: &str) -> bool {
let host = host.split('/').next().unwrap_or(host).trim();
host.starts_with("10.") || host.starts_with("192.168.")
}
fn ws_needs_auth(host: &str, auth_token: &str) -> bool {
!auth_token.is_empty() && (is_loopback_host(host) || is_private_host(host))
}
fn ws_url(host: &str, port: u16, auth_token: &str) -> String {
if ws_needs_auth(host, auth_token) {
format!(
"ws://{}:{}/?auth={}",
host,
port,
encode_query_component(auth_token)
)
} else {
format!("ws://{}:{}/", host, port)
}
}
fn send_chat_remote(
auth_token: &str,
host: &str,
@@ -82,16 +104,7 @@ fn send_chat_remote(
});
for port in ports {
let url = if is_loopback_host(host) && !auth_token.is_empty() {
format!(
"ws://{}:{}/?auth={}",
host,
port,
encode_query_component(auth_token)
)
} else {
format!("ws://{}:{}/", host, port)
};
let url = ws_url(host, *port, auth_token);
let Ok((mut socket, _)) = connect(&url) else {
continue;
@@ -99,7 +112,7 @@ fn send_chat_remote(
if let MaybeTlsStream::Plain(tcp) = socket.get_ref() {
let _ = tcp.set_read_timeout(Some(Duration::from_millis(3000)));
}
if is_loopback_host(host) && !auth_token.is_empty() {
if ws_needs_auth(host, auth_token) {
let _ = socket.send(Message::Text(
json!({ "type": "auth", "token": auth_token }).to_string(),
));
+12
View File
@@ -519,6 +519,18 @@ impl ServiceClient {
Self::webhub_host_from_local_conf().unwrap_or_else(|| String::from("127.0.0.1"))
}
/// Host for the local Secure Chat WebHub client. On macOS the daemon is reached on loopback
/// even when webhubBind is set to an overlay address for inbound peers.
pub fn webhub_local_host(&self) -> String {
#[cfg(target_os = "macos")]
{
if self.local_webhub_reachable() {
return String::from("127.0.0.1");
}
}
self.webhub_host()
}
fn webhub_port_from_local_conf() -> Option<u16> {
for path in [crate::GLOBAL_SERVICE_HOME_V2, crate::GLOBAL_SERVICE_HOME_V1] {
let conf_path = Path::new(path).join("local.conf");