Skip to content
Draft
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
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ RUN npm ci --no-audit --no-fund \
&& rm -rf /opt/weharbor/analysis/frontend /opt/weharbor/analysis/data /opt/weharbor/analysis/output /opt/weharbor/analysis/.git
COPY runtime/harness/package*.json /opt/weharbor/harness/
WORKDIR /opt/weharbor/harness
RUN npm ci --legacy-peer-deps --omit=dev --no-audit --no-fund \
RUN npm ci --omit=dev --no-audit --no-fund \
&& node node_modules/@deepseek-ai/dsh-subprocess-local/scripts/ensure-spawn-helper.mjs \
&& node -e "require('node-pty'); require('koffi')"

Expand Down Expand Up @@ -58,6 +58,7 @@ RUN test "${TARGETARCH:-amd64}" = amd64 \
dbus-x11 gnome-keyring libsecret-1-0 inotify-tools stalonetray \
python3-dbus python3-xlib python3-gi gir1.2-atspi-2.0 at-spi2-core \
python3-venv python3-dev build-essential gdb sudo \
tesseract-ocr tesseract-ocr-chi-sim \
libatomic1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 \
libxcb-render-util0 libxcb-shape0 libxcb-xinerama0 libxcb-xkb1 \
libxkbcommon-x11-0 libnss3 libgbm1 libasound2t64 \
Expand Down
161 changes: 153 additions & 8 deletions docs/workspace-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,136 @@

Validated on an amd64 Linux NAS with an existing WeHarbor profile. Test output
contains counts and status only; keys, chat contents and account identities are
not committed. The locally deployed image is
not committed. The preceding baseline image was
`sha256:5e7b6ef5b43e12e135251d23f51076e6b81a540c507a43e2c59584f67ceb5d7c`.

## October 20 update
## October 20 navigation and recovery validation

- Passed 118 integration/source tests, covering exact-result navigation, duplicate
titles, exclusion of history/message results, draft and manual-search
preservation, structured recovery, pre-input retry, context cancellation,
expiration during navigation and no retry after attempted composer writes.
- On the unlocked NAS, the new driver opened the authorized target from the
Contacts page and verified its exact original-process session. It cleared only
its own search query. An owned synthetic draft blocked navigation and readiness
checks and remained unchanged; after removing only that draft, readiness
recovered. No message was sent during these navigation/recovery checks.
- MCP now exposes fourteen tools, including read-only desktop readiness and
exact-target navigation. Scheduled transient pre-input failures may recover
for at most five minutes after due time; changed context cancels the action.
Expiry is checked again before editor writes and submission.
- Authenticated HTTP and MCP both navigated to the exact target. Missing
authentication, foreign origins and invalid request schemas were rejected.
Two synthetic operations were prepared and cancelled without submitting;
replaying a cancelled commit kept it cancelled, and the composer stayed empty.
The assistant workspace resolves the updated portable Skill from its source.
- The production workspace was updated in place while retaining the unlocked
original WeChat process. Browser checks at 1280px and 390px confirmed ready
status and no page overflow; the action table scrolls inside its card. An
unauthenticated Tailscale daemon can return null IPs; both the status response
and UI now handle that state without interrupting action-list rendering.
- Harness was restarted separately to refresh MCP discovery; its plugin inventory
reports the WeHarbor MCP plugin active. The original WeChat process was
preserved, and no model request was made. Rechecking the three earlier approved
sends found each marker exactly once; completed commit replays stayed sent,
the composer was empty and a mismatched exact username was rejected.
- Image `sha256:3d74e20955a0c6349003355451c630859e30eba3172d6d12a8749655088ed332`
was built from application revision
`c730ca0137289016e5b68efb31ffbad7807b56b5`.
Its full fresh-profile smoke test passed, including container restart,
authentication, origin rejection, app switching, fourteen MCP tools,
structured logged-out readiness, Harness RPC and original vendor file checks.
- The final image is loaded on the NAS. Nine updated runtime files match its
hashes. The persistent override selects `weharbor:realtime-20261020-navigation`
for the next recreation. The live container retains base image `b8fa1b571a93`
and its original unlocked WeChat process; it was not recreated.

## October 20 unlocked-chat validation

- After the owner unlocked WeChat and selected the authorized one-to-one chat,
original-process session proof and the unique database contact title matched.
Qt marks both search and composer as multiline, so the driver now selects the
unique editor within the titled chat pane. GI text/component/action calls use
explicit interface dispatch to avoid Accessible method-name collisions.
- The installed WeChat paints Send without an AT-SPI button. The driver now
recognizes the enabled caption locally with Tesseract, scoped to the right
composer toolbar and green button. The two Chinese words scored above 93%
in the deployment probe; acceptance requires at least 80% per word. X11/Qt
frame geometry corrects the observed 2x DPI scale, and movement cancels clicks.
- One initial attempt stopped before submission when Send was absent from
AT-SPI. Its action remains failed. Recovery checked that failure, exact owned
test draft, session identity and visible Send, and cleared only that draft.
No mouse send event occurred for that failed attempt.
- All three authorized marker messages then completed through HTTP prepare and
commit. Each has a unique new self-message database receipt and occurs once
in the selected chat. Replaying each completed commit kept its sent status
and did not add a message. The composer is empty. A mismatched username was
rejected by the current-session proof before typing.
- From observing the persisted submitted state to API visibility, bounded
probes measured 1,791 ms, 1,735 ms and 2,082 ms. Polling adds uncertainty of
about one 100 ms interval. The first two appeared before the commit receipt's
two-second refresh; the third can include that refresh. These are outgoing
self-message visibility checks, not independent incoming-message latency,
recipient delivery or read receipts.
- Passed 98 integration/source tests, including low-confidence or incorrect
Send captions and cancellation before all mouse events when a window moves.
Image `sha256:cd573b40b1a19509e0a78397d9cdc479f86cd72aa1453e2b7ed1e5712661515e`
was built from application revision `22b9f4b2c4b42c1dc294c39ea588d19dc5be8e87`.
Its local fresh-profile smoke test passed, including restart, authentication,
MCP discovery, app switching and unchanged original vendor package files.
The image's English and Simplified Chinese OCR data are installed.
- The production driver was updated in place and only the workspace service
restarted, preserving the unlocked WeChat process. The running container
remains based on image `b8fa1b571a93`; its two driver source hashes and OCR
dependencies match the new image. The full new image was loaded on the NAS
and `compose.workspace.yml` now selects `weharbor:realtime-20261020-qt` for
the next recreation. No container recreation occurred in this validation.

## October 20 event-sync and Harness update

- Analysis fork pinned to `f9be0a3a1bd0e9abfff5d1563ea29dbb35f28b24`: Linux
DB/WAL notifications debounce for 150 ms, with a two-second fallback scan.
Revision SSE checks run every 200 ms. The target is 1–3 seconds after a
stable database write; decryption duration still limits actual latency.
- Passed 70 targeted analysis tests and 92 integration/source tests. The
filesystem test exercises writes and rename replacement, while existing
tests cover authenticated decryption, WAL recovery and publication failures.
- DSH `0.2.1-rc.2` was the official npm latest on October 20, 2026. A fresh
local DSH profile successfully initialized the NAS MCP server and discovered
twelve tools, including send preparation. No tool call or model request was
made. The proxy now supports signed browser sessions, scoped cookies and
the current settings RPC while preserving foreign-origin rejection.
- Browser verification against the official DSH bundle found that logical
RPC channels must not be rewritten by the subpath proxy. JavaScript now
passes through unchanged; a regression test exercises channel validation
and one-time base-URI resolution. A fresh browser profile connects and
displays model configuration without making a model request.
- Final image `sha256:b8fa1b571a93db07da9513678854382552722709c8bc55d7232fd1dfd36a10f8`
was built from application revision `d248e9875f8421e34369cb90380f7b79381159de`.
Fresh-profile smoke tests passed locally and on the NAS, including restart,
browser authentication, origin rejection, MCP discovery and unchanged
original WeChat/Alpha package files. The existing-profile container was
replaced with this image and reports healthy.
- Production checks show filesystem-event monitoring with a two-second
fallback, SSE ready, 26 synchronized databases and zero failed accounts.
The actual NAS browser connects without a reconnect banner or console
errors, and opens the API-key form. No key or model was configured. The
Harness MCP plugin is active and exposes twelve tools. Protected integration
state fingerprints match the backup.
- An isolated 8 KiB encrypted DB/WAL fixture on the NAS completed three
authenticated decrypt/validated snapshot/SSE updates in 403 ms each. This
verifies the synthetic event path; it does not establish latency for real
messages or larger databases.
- Current-chat proof derives the session-name field from the installed
original WeChat ELF metadata and handler code. It reads process memory
without attaching a debugger or writing memory. Only an exact first-two-
component version match is eligible; unsupported layouts fail closed. Live
QObject ownership, unique page, stable reads, account, username and visible
title are checked before UI actions.
- At the earlier image rollout the desktop asked for phone confirmation. The
later unlocked-chat results above supersede that pending send check.

## October 20 earlier baseline

- Analysis fork pinned to `9ba2b71dc1b8018ab7feb59b028713beeb314f1b`.
Linux snapshots publish revisions only after validated database replacement;
Expand Down Expand Up @@ -66,12 +192,10 @@ The previous image remains tagged locally as `weharbor:before-realtime-20261020`

## Pending deployment checks

The running desktop exposes 169 accessibility nodes and asks for phone
confirmation. No real message was prepared or sent. The independent AT-SPI
driver still needs validation against a logged-in real chat, with an explicitly
authorized recipient and text. It currently requires that target chat to be
selected; it does not automatically navigate arbitrary chats or attachments.
Synthetic receipts do not establish real recipient delivery.
The driver has now passed the authorized real-chat tests above and exact-target
navigation through the accessible Contacts search results. It does not send
attachments, quoted replies, mentions or forwards. Independent incoming-message latency, recipient delivery
and read receipts remain unverified.

Model dialogue awaits Harness model credentials. Tailscale login and named
Cloudflare Tunnel credentials/DNS await the owner's configuration. Their
Expand All @@ -89,6 +213,27 @@ selects the locally built image without pulling it. The original profile volume
is preserved. Before deploying, the original Compose configuration and private
container metadata were backed up, and the previous image was tagged locally.
Keep that backup and image until the pending account/model/network checks pass.
The event-sync rollout backup is `event-sync-20261020-064151` under the private
deployment backup directory. The preceding image remains tagged
`weharbor:before-fast-sync-20261020`; the intermediate Harness integration
image is also retained as `weharbor:before-harness-fix-20261020`.
The Qt Send update backup is `qt-send-20261020-081901` in that directory, with
the preceding override and a hash-only image/source manifest. Its base image
is retained as `weharbor:before-qt-send-20261020`. The in-place desktop driver
backup is `/config/.local/share/weharbor/desktop-before-qt-controls.py` inside
the production container. Recreating from the selected new image preserves the
profile volume but restarts WeChat, so it was staged rather than forced while
the owner's unlocked chat was in use.

The later navigation rollout backup is `navigation-20261020-102426` under the
same private deployment backup directory. It retains the preceding override
and hash-only manifest. `weharbor:before-navigation-20261020` retains the Qt
image. The pre-navigation runtime files and private action-database backup are
inside `/config/.local/share/weharbor/before-navigation-20261020-100304` in the
production container. A runtime-only rollback should restore the saved program
files while the workspace service is stopped, then restart that service; do not
replace the live action database with an older backup. This preserves subsequent
operation records and the unlocked WeChat session.

No combined image was published. Upstream analysis licensing and the vendor
redistribution conditions in `THIRD_PARTY_NOTICES.md` still require clarification
Expand Down
55 changes: 46 additions & 9 deletions docs/workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ The pinned fork is `fliaping/WeChatDataAnalysis`; the source revision and archiv
checksum are in `versions.lock.json`. The Linux adapter first uses existing
account-specific WeFlow keys, verifies decrypted SQLite/WAL snapshots, and
verifies image AES/XOR keys against actual V2 images. Original encrypted files
are never overwritten. Refresh runs every 30 seconds and can be triggered in
the UI or through MCP. Decrypted copies and verified keys remain under
are never overwritten. A serialized Linux DB/WAL watcher refreshes changed
files, with a two-second recovery scan; refresh can also be triggered in the
UI or through MCP. Decrypted copies and verified keys remain under
`/config/.local/share/weharbor/analysis`, with private permissions.
Linux chat auto-update uses snapshot revision events over SSE: after a validated
sync, the page refreshes sessions and the selected conversation. Reconnecting
also refreshes the page data. This is periodic synchronization (roughly 30 seconds
plus decryption time), not a native WCDB connection; imported archives remain
also refreshes the page data. Linux uses inotify with a 150 ms debounce and
SSE revision checks every 200 ms, targeting 1–3 seconds after source writes.
This is validated snapshot synchronization, not a native WCDB connection;
decryption, busy files and host load can delay it. Imported archives remain
static. Unchanged sync attempts do not trigger repeated chat refreshes.

If no usable key exists, the Linux source card offers an explicit wcdb-key-tool
Expand Down Expand Up @@ -59,21 +62,55 @@ auth. Foreign browser Origin headers are rejected.

## AI and Actions

Official `@deepseek-ai/dsh` is pinned through npm's integrity lock. The web
Official `@deepseek-ai/dsh` 0.2.1-rc.2 (npm `latest`, checked 2026-10-20) is
pinned through npm's integrity lock. The web
launcher adds the local WeHarbor MCP client, and the assistant workspace contains
the portable skill. Configure your model credentials in Harness's own setup UI;
WeHarbor does not copy Alpha/WeFlow credentials into Harness. Its state is
`/config/.local/share/weharbor/harness`.
The loopback proxy exchanges DSH's process-scoped launch token on the server
and scopes its signed HttpOnly cookie to `/assistant/`. The token is redacted
from logs. Original browser origins are checked before forwarding HTTP and
WebSocket traffic; the external browser authentication remains required.

Read, search, summarize and suggest replies use message MCP tools. Sending is a
separate prepare/commit action with an exact username, idempotency key, context
revision, desktop checks and persistent receipt. Scheduling uses the same
checks and requires the selected target chat to remain ready at the due time.
checks and automatically opens and verifies the exact target at the due time.
Preparation alone never schedules a send: commit activates a future action.
Only transient failures before a composer write may wait for recovery; scheduled
actions retry every five seconds for at most five minutes after their due time.
An immediate action retains its original 120-second lifetime. Context changes
cancel execution, and any failure after an attempted composer write prevents
automatic retry. Submitted or uncertain actions are never retried.

The fourteen MCP tools include read-only `wechat_desktop_status` and
`wechat_open_chat`, which opens an exact account/username without typing a
message. The Connections page shows desktop readiness, recovery instructions
and blocked action reasons, with bounded retry and cancellation controls.
Navigation uses only exact labels in accessible Contacts/Groups search results,
never history results. It checks active X11 geometry before clicking and verifies
the original-process session after opening. Search queries are cleared only if
they still exactly match the driver's own input. Manual input is preserved.
The independent AT-SPI driver does not patch WeChat or copy Qibox code/offsets.
It verifies the open account from process file descriptors and binds the visible
chat using unique DB contact names plus an ID or recent message fingerprints.
Unsupported or ambiguous layouts fail closed. Existing drafts and manual input
Qt's search editor is disambiguated inside the titled chat pane. For the painted
Send button absent from AT-SPI, local Tesseract OCR must recognize one complete
Send caption with at least 80% confidence per word inside the composer's right
toolbar region and enabled green button. X11 geometry corrects Qt high-DPI
coordinates, and a moved or changed window prevents clicking. No screenshot or
chat text is sent to an OCR service.
It verifies the open account from process file descriptors. A fixed root helper
reads the original WeChat process without writing or injecting code. Qt metadata
in the installed ELF identifies the session-loaded handler and derives the
current session-name field; a live QObject backlink, runtime code checks and
stable reads reject stale or racing heap data. Exactly one live typed chat page
must resolve the requested username, and its visible chat title must match
the database contact with a unique composer. A globally unique nickname is not
required because the exact process session supplies identity. Account and session
are checked again before submission.
Version eligibility compares the first two numeric components, without a major
version ceiling; it never replaces structural validation. No external offset
table is copied. Unsupported or ambiguous layouts fail closed. Existing drafts and manual input
are preserved; an interrupted submission is never retried automatically.

A `sent` receipt means the new self-message appeared in the database, not
Expand Down
Loading
Loading