Updates
CI / test (push) Successful in 1m3s
Release rolling / release (push) Has been cancelled

This commit is contained in:
Raven Scott
2026-07-18 23:32:56 -04:00
parent 6ef3afe821
commit 4633210eea
13 changed files with 64 additions and 25 deletions
+3 -2
View File
@@ -15,8 +15,9 @@ PEARDATA_DEFAULT_ROLE=viewer
# PEARDATA_DATA_DIR=./data
# PEARDATA_RATE_LIMIT_RPM=120
# PEARDATA_MAX_RECONNECT=20
# Host journal for Logs tab / GET /api/v*/logs?source=journal (Linux; needs journal ACL)
# PEARDATA_JOURNAL=0
# Host journal for Logs tab / GET /api/v*/logs?source=journal (on by default on Linux)
# Installer adds peardata to systemd-journal. Set to 0 to disable.
# PEARDATA_JOURNAL=1
# ── Metrics pipeline ─────────────────────────────────────────
# PEARDATA_SAMPLE_MS=1000
+4 -2
View File
@@ -14,12 +14,14 @@ TimeoutStartSec=30
TimeoutStopSec=30
User=peardata
Group=peardata
# Host journal for Logs tab (journalctl). Installer also adds peardata to this group.
SupplementaryGroups=systemd-journal
NoNewPrivileges=true
PrivateTmp=true
Environment=NODE_ENV=production
# Journal on by default; override in .env with PEARDATA_JOURNAL=0 to disable
Environment=PEARDATA_JOURNAL=1
EnvironmentFile=-/opt/peardata/.env
# Optional host journal in Logs tab: set PEARDATA_JOURNAL=1 in .env and uncomment:
# SupplementaryGroups=systemd-journal
# Identity + HyperDB / metric buffers
ReadWritePaths=/opt/peardata
+1 -1
View File
@@ -40,7 +40,7 @@ Treat `SERVER_SEED` like a root password. Prefer `pd1.` invites for operators.
| `PEARDATA_STORAGE` | — | Electron/Pear storage dir override (bookmarks prefer `Pear.config.storage`) |
| `PEARDATA_RATE_LIMIT_RPM` | `120` | Per-peer RPC requests per minute |
| `PEARDATA_MAX_RECONNECT` | `20` | Client manager reconnect attempts per peer |
| `PEARDATA_JOURNAL` | off | `1` enables host `journalctl` for Logs / `queryLogs` (Linux; needs journal ACL) |
| `PEARDATA_JOURNAL` | **on** | Host `journalctl` for Logs / `queryLogs` (Linux). Set `0` to disable. Installer adds `peardata` to `systemd-journal` and sets unit `SupplementaryGroups` |
```
data/
+3 -1
View File
@@ -110,12 +110,14 @@ npm start
## systemd (Linux)
Prefer the [one-line installer](#one-line-installer-recommended) (`--server`). It installs the Bare binary and writes `peardata.service`.
Prefer the [one-line installer](#one-line-installer-recommended) (`--server`). It installs the Bare binary, writes `peardata.service`, adds `peardata` to **`systemd-journal`**, and enables host journal for the Logs tab (`PEARDATA_JOURNAL=1`).
Manual (matches installer layout):
```bash
# After placing peardata-server + .env under /opt/peardata
sudo useradd --system --home /opt/peardata --shell /usr/sbin/nologin peardata # if needed
sudo usermod -aG systemd-journal peardata
sudo cp deploy/peardata.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now peardata
+1 -1
View File
@@ -82,7 +82,7 @@ REST parity: `GET /api/v*/weights`. Engine: `server/services/weights.js`.
| `priority` / `unit` | Journal filters |
| `limit` / `cursor` | Cap (≤2000) + pagination offset |
Journal requires `PEARDATA_JOURNAL=1` on Linux. REST: `GET /api/v*/logs`. Engine: `server/services/logs.js`. UI: [user-guide/logs.md](../user-guide/logs.md).
Journal is **enabled by default** on Linux (`PEARDATA_JOURNAL=0` to disable). Installer grants `systemd-journal` to the agent user. REST: `GET /api/v*/logs`. Engine: `server/services/logs.js`. UI: [user-guide/logs.md](../user-guide/logs.md).
### Live subscriptions
+2 -2
View File
@@ -47,7 +47,7 @@
- [ ] Run under systemd with `ProtectSystem` / `NoNewPrivileges` (see `deploy/`)
- [ ] Do not embed seed in frontend builds, CI logs, or crash reports
- [ ] Review `LOG_LEVEL=debug` before production (avoid verbose auth noise)
- [ ] Keep `PEARDATA_JOURNAL` off unless operators need host logs; journal access is **admin-only** over P2P and can expose secrets from other units — add `SupplementaryGroups=systemd-journal` only when enabling
- [ ] Journal is **on by default** (admin-only over P2P); it can expose secrets from other units — set `PEARDATA_JOURNAL=0` if you do not want host log access
- [ ] Keep Pear / dependency updates current (`npm outdated`)
## Threat notes
@@ -86,7 +86,7 @@ Implementation: `shared/crypto-auth.js`.
| `.env` | Critical | Never commit; backup offline |
| `data/peer-policy.json` | High | Contains roles & JTIs |
| `data/audit.log` | Medium | Peer activity metadata; readable via Logs/audit (admin) |
| Host journal (`PEARDATA_JOURNAL`) | High | May include secrets from other units; admin-only; off by default |
| Host journal (`PEARDATA_JOURNAL`) | High | May include secrets from other units; admin-only; **on by default** (disable with `=0`) |
| `identity.json` | High for that user | Per-machine client secret |
| Release tarballs | Low | Source only; no secrets |
+1 -1
View File
@@ -89,7 +89,7 @@ When adding an RPC method:
| Metric Correlations | Charts → Correlate → brush ≥15s → Find Correlations → wall filters; Clear restores; Related ⇢ still works |
| Charts Filters | Filters closed → full-width wall; `/` opens search; chip when filter active; Esc closes panel |
| Weights REST | `curl -sG 'http://127.0.0.1:19999/api/v3/weights' --data-urlencode 'method=volume' --data-urlencode 'after=-60' --data-urlencode 'before=0'` |
| Logs | Logs tab → Anomalies search; admin → Audit; with `PEARDATA_JOURNAL=1` → Journal |
| Logs | Logs tab defaults to Journal (admin); Anomalies for viewers; Audit admin-only |
| Logs REST | `curl -sG 'http://127.0.0.1:19999/api/v3/logs' --data-urlencode 'source=anomaly' --data-urlencode 'limit=20'` |
## Soak test
+2 -2
View File
@@ -278,9 +278,9 @@
</header>
<div class="logs-toolbar">
<div id="logs-sources" class="logs-sources" role="tablist" aria-label="Log source">
<button type="button" class="metrics-tf active" data-log-source="anomaly" role="tab" aria-selected="true">Anomalies</button>
<button type="button" class="metrics-tf active" data-log-source="journal" role="tab" aria-selected="true" title="Admin only · host journal (default)">Journal</button>
<button type="button" class="metrics-tf" data-log-source="anomaly" role="tab" aria-selected="false">Anomalies</button>
<button type="button" class="metrics-tf" data-log-source="audit" role="tab" aria-selected="false" title="Admin only">Audit</button>
<button type="button" class="metrics-tf" data-log-source="journal" role="tab" aria-selected="false" title="Admin only · PEARDATA_JOURNAL=1">Journal</button>
</div>
<div class="logs-search-row">
<input id="logs-q" type="search" placeholder="Search message…" autocomplete="off" />
+27
View File
@@ -321,9 +321,33 @@ install_server() {
fi
id peardata >/dev/null 2>&1 || die "peardata user was not created"
# Host journal access for Logs tab (journalctl as peardata)
if getent group systemd-journal >/dev/null 2>&1; then
log "Adding peardata to systemd-journal for host log access…"
if command -v usermod >/dev/null 2>&1; then
run_root usermod -aG systemd-journal peardata || warn "usermod -aG systemd-journal peardata failed"
elif command -v gpasswd >/dev/null 2>&1; then
run_root gpasswd -a peardata systemd-journal || warn "gpasswd -a peardata systemd-journal failed"
else
warn "Could not add peardata to systemd-journal (no usermod/gpasswd)"
fi
else
warn "Group systemd-journal not found — host Journal in Logs may be unavailable"
fi
# Ensure journal is enabled in .env (default on; operators may set PEARDATA_JOURNAL=0)
if ! run_root grep -qE '^PEARDATA_JOURNAL=' "${SERVER_DIR}/.env" 2>/dev/null; then
log "Enabling PEARDATA_JOURNAL=1 in ${SERVER_DIR}/.env"
run_root sh -c "printf '\\n# Host journal for Logs tab (set to 0 to disable)\\nPEARDATA_JOURNAL=1\\n' >> '${SERVER_DIR}/.env'"
fi
run_root chown -R peardata:peardata "$SERVER_DIR" 2>/dev/null || run_root chown -R peardata "$SERVER_DIR"
unit_path="/etc/systemd/system/peardata.service"
local journal_group_line=""
if getent group systemd-journal >/dev/null 2>&1; then
journal_group_line="SupplementaryGroups=systemd-journal"
fi
log "Writing ${unit_path}"
run_root tee "$unit_path" >/dev/null <<EOF
[Unit]
@@ -342,9 +366,11 @@ TimeoutStartSec=30
TimeoutStopSec=30
User=peardata
Group=peardata
${journal_group_line}
NoNewPrivileges=true
PrivateTmp=true
Environment=NODE_ENV=production
Environment=PEARDATA_JOURNAL=1
EnvironmentFile=-${SERVER_DIR}/.env
# Identity + HyperDB / ring buffers
ReadWritePaths=${SERVER_DIR}
@@ -380,6 +406,7 @@ ${C_BOLD}Server installed${C_RESET}
Config: ${SERVER_DIR}/.env ${C_BOLD}← check this file for your keys${C_RESET}
Data: ${SERVER_DIR}/data
Service: peardata.service
Journal: peardata ∈ systemd-journal · PEARDATA_JOURNAL=1 (Logs tab)
REST: http://127.0.0.1:19999/api/v3/info (localhost by default)
${C_BOLD}Keys (written on first successful start)${C_RESET}
+7 -3
View File
@@ -209,9 +209,13 @@ export function buildJournalArgv(opts = {}) {
return args
}
/** Host journal is on by default; set PEARDATA_JOURNAL=0 to disable. */
export function isJournalEnabled() {
const v = String(process.env.PEARDATA_JOURNAL || '').trim().toLowerCase()
return v === '1' || v === 'true' || v === 'yes' || v === 'on'
const raw = process.env.PEARDATA_JOURNAL
if (raw == null || String(raw).trim() === '') return true
const v = String(raw).trim().toLowerCase()
if (v === '0' || v === 'false' || v === 'no' || v === 'off') return false
return true
}
/**
@@ -307,7 +311,7 @@ export async function queryLogs(args = {}, deps = {}) {
ok: false,
source,
error: 'journal_disabled',
hint: 'Set PEARDATA_JOURNAL=1 and ensure the agent user can read the journal (e.g. SupplementaryGroups=systemd-journal)',
hint: 'Journal disabled (PEARDATA_JOURNAL=0). Re-enable and ensure the agent user is in systemd-journal (installer / SupplementaryGroups)',
entries: [],
}
}
+4 -2
View File
@@ -160,9 +160,12 @@ test('queryLogs audit from temp file', async (t) => {
t.ok(res.entries[0].message.includes('runJob'))
})
test('queryLogs journal disabled / unsupported', async (t) => {
test('queryLogs journal default on / disable / unsupported', async (t) => {
const prev = process.env.PEARDATA_JOURNAL
delete process.env.PEARDATA_JOURNAL
t.ok(isJournalEnabled(), 'journal enabled by default')
process.env.PEARDATA_JOURNAL = '0'
t.absent(isJournalEnabled())
const off = await queryLogs({ source: 'journal', role: Roles.admin })
t.absent(off.ok)
@@ -175,7 +178,6 @@ test('queryLogs journal disabled / unsupported', async (t) => {
t.absent(uns.ok)
t.is(uns.error, 'unsupported')
} else {
// On Linux with journal enabled, inject a fake spawn that fails cleanly
const res = await queryLogs(
{ source: 'journal', role: Roles.admin, limit: 5 },
{
+2 -1
View File
@@ -24,7 +24,8 @@ import { Roles, roleAllows } from '../shared/protocol.js'
*/
export function createLogsView(opts) {
const state = {
source: 'anomaly',
/** Prefer host journal; falls back to anomaly when not admin */
source: 'journal',
range: '1h',
loading: false,
}
+7 -7
View File
@@ -8,7 +8,7 @@ The **Logs** tab searches recent events from the active agent: anomalies, the ag
|--------|----------------|---------------|
| **Anomalies** | Viewer+ | Threshold / z-score events (same family as Alerts) |
| **Audit** | Admin | Mutating RPC trail from `data/audit.log` |
| **Journal** | Admin | Host `journalctl` lines (Linux, opt-in) |
| **Journal** (default) | Admin | Host `journalctl` lines (Linux; on by default) |
Audit and Journal tabs are disabled for non-admin sessions (tooltip explains why).
@@ -22,15 +22,15 @@ Audit and Journal tabs are disabled for non-admin sessions (tooltip explains why
Results show severity, message (monospace), time, and unit. Anomaly rows offer **Show** / **Correlate** like the Alerts tab.
## Enabling host journal
## Host journal (default on)
Default is **off** (no `journalctl` spawn).
The one-line / `--server` installer:
1. Set `PEARDATA_JOURNAL=1` on the agent.
2. Ensure the agent user can read the journal (often `SupplementaryGroups=systemd-journal` in the systemd unit).
3. Restart the agent.
1. Adds the `peardata` user to the **`systemd-journal`** group
2. Sets unit **`SupplementaryGroups=systemd-journal`**
3. Writes **`PEARDATA_JOURNAL=1`** into `/opt/peardata/.env` (and the unit Environment)
Without this, Journal returns a clear “disabled” hint. Non-Linux agents always report journal as unsupported.
To disable: set `PEARDATA_JOURNAL=0` in `.env` and restart. Non-Linux agents report journal as unsupported.
See [CONFIGURATION](../docs/CONFIGURATION.md) and [SECURITY](../docs/SECURITY.md).