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
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,14 @@ scripts/*
downloads/*
!downloads/alpha.deb
!downloads/wechat.deb
!downloads/analysis.tar.gz
!downloads/node.tar.xz
!downloads/tailscale.tgz
!downloads/cloudflared
!runtime/
!runtime/harness/
!runtime/harness/package.json
!runtime/harness/package-lock.json
!skills/
!skills/weharbor-api/
!skills/weharbor-api/**
4 changes: 3 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ jobs:
with:
python-version: '3.12'
- name: Validate source and test asset handling
run: python3 scripts/check.py
run: |
pip install -r requirements-workspace-dev.txt
python3 scripts/check.py
- name: Validate Compose
env:
PASSWORD: validation-only
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ __pycache__/
*.AppImage
*.pem
*.key
node_modules/
.output/
41 changes: 38 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@ COPY root/ /integration/
RUN find /integration -type d -exec chmod 0755 {} + \
&& find /integration -type f -exec chmod 0644 {} +

FROM ${BASE_IMAGE} AS workspace-runtime
COPY downloads/node.tar.xz downloads/analysis.tar.gz downloads/tailscale.tgz downloads/cloudflared /tmp/assets/
COPY versions.lock.json /tmp/versions.lock.json
RUN python3 -c "import hashlib,json,pathlib; lock=json.load(open('/tmp/versions.lock.json')); [(lambda p,c: (None if hashlib.sha256(p.read_bytes()).hexdigest()==c['sha256'] else (_ for _ in ()).throw(ValueError(str(p)))))(pathlib.Path('/tmp/assets')/lock[n]['filename'],lock[n]) for n in ['node','analysis','tailscale','cloudflared']]" \
&& mkdir -p /opt/weharbor/node /opt/weharbor/analysis /opt/weharbor/network \
&& python3 -c "import lzma,shutil; shutil.copyfileobj(lzma.open('/tmp/assets/node.tar.xz'),open('/tmp/node.tar','wb'))" \
&& tar -xf /tmp/node.tar --strip-components=1 -C /opt/weharbor/node \
&& tar -xzf /tmp/assets/analysis.tar.gz --strip-components=1 -C /opt/weharbor/analysis \
&& tar -xzf /tmp/assets/tailscale.tgz --strip-components=1 -C /opt/weharbor/network \
&& cp /tmp/assets/cloudflared /opt/weharbor/network/cloudflared \
&& chmod 0755 /opt/weharbor/network/cloudflared
ENV PATH="/opt/weharbor/node/bin:${PATH}"
WORKDIR /opt/weharbor/analysis/frontend
RUN npm ci --no-audit --no-fund \
&& NUXT_APP_BASE_URL=/analysis/ NUXT_PUBLIC_API_BASE=/analysis/api npm run generate \
&& mv .output/public /opt/weharbor/analysis/ui \
&& 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 \
&& node node_modules/@deepseek-ai/dsh-subprocess-local/scripts/ensure-spawn-helper.mjs \
&& node -e "require('node-pty'); require('koffi')"

FROM ${BASE_IMAGE}
ARG TARGETARCH
ARG PROJECT_VERSION=0.1.0
Expand All @@ -33,12 +56,18 @@ RUN test "${TARGETARCH:-amd64}" = amd64 \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
dbus-x11 gnome-keyring libsecret-1-0 inotify-tools stalonetray \
python3-dbus python3-xlib \
python3-dbus python3-xlib python3-gi gir1.2-atspi-2.0 at-spi2-core \
python3-venv python3-dev build-essential gdb sudo \
libatomic1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 \
libxcb-render-util0 libxcb-shape0 libxcb-xinerama0 libxcb-xkb1 \
libxkbcommon-x11-0 libnss3 libgbm1 libasound2t64 \
&& rm -rf /var/lib/apt/lists/*

COPY --from=workspace-runtime /opt/weharbor/ /opt/weharbor/
RUN /usr/bin/python3 -m venv --system-site-packages /opt/weharbor/python \
&& /opt/weharbor/python/bin/pip install --no-cache-dir -r /opt/weharbor/analysis/requirements-linux.txt \
&& /opt/weharbor/python/bin/python -c "import gi,fastapi,cryptography,pilk; gi.require_version('Atspi','2.0')"

COPY versions.lock.json /usr/share/weharbor/versions.lock.json
COPY scripts/verify-assets.py /tmp/verify-assets.py
COPY downloads/wechat.deb /tmp/wechat.deb
Expand All @@ -54,11 +83,13 @@ RUN apt-get update \
&& cp /usr/share/icons/hicolor/128x128/apps/wechat.png /usr/share/selkies/www/icon.png

COPY --from=integration-files /integration/ /
COPY skills/weharbor-api/ /usr/share/weharbor/skills/weharbor-api/
COPY LICENSE THIRD_PARTY_NOTICES.md /usr/share/doc/weharbor/
RUN test -f /defaults/default.conf \
&& chmod -R a+rX /usr/share/doc/weharbor \
&& chmod -R a+rX /usr/share/doc/weharbor /usr/share/weharbor/skills \
&& sed -i '\|location SUBFOLDERapi {|i\ include /defaults/notification-proxy.conf;' /defaults/default.conf \
&& grep -q notification-proxy.conf /defaults/default.conf \
&& sed -i '\|location SUBFOLDERapi {|i\ include /defaults/workspace-proxy.conf;' /defaults/default.conf \
&& cat /defaults/alpha-api-proxy.conf >> /defaults/default.conf \
&& sed -i 's#</body>#<script src="./browser-notifications.js"></script></body>#' \
/usr/share/selkies/selkies-dashboard/index.html \
Expand All @@ -71,7 +102,11 @@ RUN test -f /defaults/default.conf \
/scripts/app-switcher/app-switcher.py \
/scripts/notifications/*.sh /scripts/notifications/*.py \
/scripts/alpha/*.sh /scripts/wechat/*.sh /scripts/weharbor/* \
/usr/local/bin/stalonetray /usr/local/bin/weharbor-health
/scripts/workspace/*.sh /etc/s6-overlay/s6-rc.d/svc-analysis/run \
/etc/s6-overlay/s6-rc.d/svc-workspace/run /etc/s6-overlay/s6-rc.d/svc-harness/run \
/usr/local/bin/stalonetray /usr/local/bin/weharbor-health /usr/local/bin/weharbor-capture-key \
&& chmod 0440 /etc/sudoers.d/weharbor-capture \
&& visudo -cf /etc/sudoers.d/weharbor-capture

ENV TITLE="WeHarbor|微港" \
SELKIES_UI_TITLE="WeHarbor|微港" \
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ Selkies 2.0 基础镜像(Debian trixie),把官方 Linux 微信和 Alpha
[安装为桌面应用](docs/pwa.md) ·
[组件许可](THIRD_PARTY_NOTICES.md)

集成工作区开发版:边缘挂件可全屏切换桌面、Linux 聊天分析、DeepSeek
Harness 和连接设置。支持已有 WeFlow 密钥、数据库/WAL 与图片解密、
独立 MCP/Skill,以及带回执的桌面 Action。配置和限制见
[工作区说明](docs/workspace.md)。

## 当前状态

源码版本 0.1.0,仓库为 [fliaping/weharbor](https://github.com/fliaping/weharbor)。
Expand Down Expand Up @@ -51,7 +56,7 @@ cd weharbor
docker compose up -d
~~~

build.sh 会下载并校验两个锁定版本的安装包。离线构建可提前把
build.sh 会下载并校验 versions.lock.json 中锁定的组件。离线构建可提前把
Alpha-0.2.0-x64.deb 放到 downloads/alpha.deb,把
WeChatLinux_4.1.13.23_amd64.deb 放到 downloads/wechat.deb。

Expand Down
16 changes: 15 additions & 1 deletion THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,21 @@ It does not relicense vendor applications, base-image packages or native librari
| Official Linux WeChat | https://linux.weixin.qq.com/ | Proprietary vendor distribution. The original deb is mirrored in the component-assets Release with its pinned SHA256; it is installed unmodified with dpkg. Verify redistribution terms before publishing a combined image. |
| Alpha 0.2.0 | https://github.com/xunchahaha/alpha-releases | Closed-source, unsigned binary release; the deb control file declares `License: unknown`. Installed unmodified from the pinned SHA256. Redistribution terms are unverified: do not publish a combined image until they are clarified. Bundled Electron/Chromium/ffmpeg notices remain under /opt/Alpha. |

WeHarbor does not unpack, patch or rebuild vendor packages. Image notices are
Additional workspace components:

| Component | Source | License and handling |
| --- | --- | --- |
| WeChatDataAnalysis Linux fork | https://github.com/fliaping/WeChatDataAnalysis (upstream LifeArchiveProject/WeChatDataAnalysis) | Upstream has no repository-wide LICENSE file at the pinned revision. Retain its existing notices; WeHarbor MIT does not relicense it. Source and web UI are included at /opt/weharbor/analysis. |
| wcdb-key-tool | https://github.com/TANGandXUE/wcdb-key-tool | MIT; unchanged capture tool, LICENSE and attribution included in the analysis fork. |
| DeepSeek Harness | https://github.com/deepseek-ai/deepseek-harness | MIT; official npm distributions and dependencies retain their package licenses under /opt/weharbor/harness/node_modules. |
| Tailscale | https://github.com/tailscale/tailscale | BSD-3-Clause; official archive retains its LICENSE under /opt/weharbor/network. |
| cloudflared | https://github.com/cloudflare/cloudflared | Apache-2.0; pinned official binary. |
| Node.js | https://nodejs.org/ | MIT and bundled third-party notices; official distribution retains LICENSE under /opt/weharbor/node. |

Qibox informed the Action design; no Qibox code, application files, memory
offsets, or captured user data are included in this repository.

WeHarbor does not modify the WeChat or Alpha vendor packages. Image notices are
installed at /usr/share/doc/weharbor; original application notices remain in
their vendor locations.

Expand Down
4 changes: 2 additions & 2 deletions docs/pwa.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ WeHarbor 可以安装为 PWA,以独立窗口从电脑桌面或手机主屏幕

## 电脑和 Android

用 Chrome 或 Edge 打开桌面,等页面加载后点击右下角 **安装 WeHarbor**,
用 Chrome 或 Edge 打开桌面,展开边缘的 **应用管理**,点击底部的 **安装 WeHarbor**,
确认浏览器安装对话框。也可以使用浏览器地址栏或菜单里的“安装应用”。
按钮在浏览器发出可安装事件时显示,已安装的独立窗口内会隐藏。
不支持安装事件的浏览器可尝试其“添加到主屏幕”菜单。

## iPhone 和 iPad

在 Safari 中打开桌面,点击 **安装 WeHarbor** 查看操作提示,
在 Safari 中打开桌面,展开 **应用管理**,点击底部的 **安装 WeHarbor** 查看操作提示,
然后点击 Safari 的分享按钮,选择 **添加到主屏幕**。
这一步需要用户在 Safari 中确认,网页无法代替系统执行安装。

Expand Down
95 changes: 95 additions & 0 deletions docs/workspace-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Workspace validation — 2026-10-20

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
`sha256:5e7b6ef5b43e12e135251d23f51076e6b81a540c507a43e2c59584f67ceb5d7c`.

## October 20 update

- Analysis fork pinned to `9ba2b71dc1b8018ab7feb59b028713beeb314f1b`.
Linux snapshots publish revisions only after validated database replacement;
SSE refreshes sessions and the selected chat, including after reconnect.
Linux reads report their decrypted source without attempting native WCDB.
- Passed 59 targeted backend tests covering snapshot publication, failed
decryption preservation, source selection, startup, WAL recovery and key
modes, plus one frontend event-store test. The 78 integration/source checks
also passed.
- Full final image build and fresh-profile smoke passed, including restart,
browser authentication, origin protection, twelve MCP tools, app switching
and unchanged original WeChat/Alpha packages.
- Existing profile retained; deployed container is healthy. Production checks
through nginx confirmed automatic Linux snapshot updates are available, the
refresh interval is 30 seconds, native WCDB was not probed, and the SSE ready
event reports Linux snapshot mode. All 26 databases synchronized without
failures. Read 1,029 sessions and a bounded sample of two messages; an actual
V2 image decoded and returned HTTP 200 as a 7,157-byte JPEG.
- Installation is a secondary button inside app management: 12px text, 36px
height, transparent background, no independent fixed positioning. Browser
checks with simulated install eligibility confirmed one prompt invocation,
hiding after consumption, and hiding with the menu. At 390 × 844 there was
no horizontal overflow. On ordinary NAS HTTP the button stays hidden because
the browser does not permit PWA installation there. No app was installed.

Synthetic database publication and frontend event tests verify the update
path. A newly received real message was not observed in an open chat during
this check; the production checks establish synchronization and event-stream
availability, rather than a native WCDB live connection.

The previous image remains tagged locally as `weharbor:before-realtime-20261020`.

## October 19 baseline

- Source validation: 78 tests, including preparation/commit, scheduling,
idempotency, crash recovery, uncertain receipts, chat identity, remote-reader
exclusion, local-source pinning, authenticated transport selection and failover.
- Analysis fork at `28ef68958969cd1cd7b50f49a7f03e8737ca6ee5`: 85 targeted tests
covering Linux snapshots, startup, key handling, image decryption and source
selection, with seven upstream/dependency warnings.
- Full image build and fresh-profile smoke test, including container restart,
existing browser authentication, CSRF protection, app switching, profile
permissions and unchanged original WeChat/Alpha packages.
- Authenticated Harness RPC successfully listed a directory through the public
prefix; foreign-origin RPC returned 403. The HTML compatibility layer supplies
randomUUID on NAS HTTP without changing the official Harness bundle.
- Browser at 390 × 844: fullscreen view switching, persistent iframes, no
horizontal overflow in the tested views, working analysis agreement page, and
Harness directory selection plus creation of an empty synthetic session.
The analysis agreement was displayed without accepting it on the user's behalf.
- Existing-profile read-only validation: 26 databases synchronized with zero
failures; one account, 1,028 sessions and a bounded sample of two messages read.
Existing WeFlow DB and image keys were reused and verified. One actual V2 image
decoded as a 2,839-byte JPEG and returned HTTP 200 through the media API.
Generated avatar URLs preserve `/analysis/api/`.
- Twelve MCP tools listed; the portable Skill loaded through HTTP and MCP
resources. Harness performed MCP initialization and tool discovery.

## 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.

Model dialogue awaits Harness model credentials. Tailscale login and named
Cloudflare Tunnel credentials/DNS await the owner's configuration. Their
binaries and settings UI are present; no working external route is claimed.
Direct NAS HTTP was verified; a separate public domain was not configured.

Existing WeFlow keys were sufficient, so GDB capture/re-login was not exercised
against the production account. Linux capture and image paths have targeted
tests; not every historical attachment format was tested on real data.

## Deployment and rollback

The existing Compose file is kept intact; a `compose.workspace.yml` override
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.

No combined image was published. Upstream analysis licensing and the vendor
redistribution conditions in `THIRD_PARTY_NOTICES.md` still require clarification
before publishing one.
Loading
Loading