3105-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@3105-mcpextract test.3105 with password JFL"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
3105-extract
Công cụ CLI + MCP server dùng để xem, mở khóa, giải nén, kiểm tra và tạo ra
gói patch .3105 — định dạng patch của ứng dụng iOS
3105, dùng để sửa dữ liệu bên trong container
của app và App Group trên chính thiết bị của bạn.
Viết lại bằng Node.js thuần cho Termux/Linux/macOS, nên bạn làm được mọi thứ ngay trong terminal — hoặc để một AI agent làm thay bạn qua MCP server cục bộ.
3105PATCH\0 + binary plist envelope + AES-256-GCM wrapped key & payload1. Giới thiệu
1.1 3105 và .3105 là gì?
3105 là một ứng dụng iOS cho phép bạn gom nhiều thay đổi vào một gói patch, rồi áp gói đó lên container của một app hoặc một App Group trên máy bạn — không cần jailbreak, không cần máy tính, không có gì gửi lên máy chủ của Apple.
Một gói patch .3105 là một file nhị phân, tự chứa, gồm 3 phần:
+--------------------+------------------------------+-------------------------------+
| 3105 P A T C H \0 | bplist00 envelope | (ciphertext nằm bên trong) |
| 10 byte ASCII | binary property list | |
+--------------------+------------------------------+-------------------------------+Magic
3105PATCH\0— 10 byte đầu tiên: 9 ký tự ASCII3105PATCHcộng một byte NUL (00). Magic không nằm trong plist, nên mọi trình đọc đều phải kiểm tra 10 byte này trước đã. Đây là cách nhanh nhất để biết "đây có phải file.3105không".Envelope — một binary property list (
bplist00) chứaschemaVersion,packageID,isPasswordProtected,keyFingerprint,wrappedContentKey,kdfSalt,kdfIterations,encryptedPayload. Phần này luôn đọc được không cần mật khẩu.Payload đã mã hóa — blob
AES-256-GCMchứa toàn bộ nội dung patch (danh sách rule + dữ liệu thay thế), chỉ mở được khi có content key.
Mật khẩu chỉ bảo vệ content key, không bảo vệ envelope. Đó là lý do info và
scan chạy được trên file có mật khẩu mà không cần nhập gì.
Chi tiết byte-level của toàn bộ định dạng: docs/FORMAT.md.
1.2 Công cụ này làm được gì?
Khả năng | Nghĩa là |
Inspect — Xem | Đọc metadata công khai mà không cần mật khẩu: magic, schema version, package ID, cờ có mật khẩu, tham số KDF, key fingerprint. |
Unlock — Mở khóa | Dẫn xuất content key từ mật khẩu rồi đối chiếu với |
Extract — Giải nén | Giải mã gói thành một workspace có thể sửa tay ( |
Verify — Kiểm tra | Kiểm tra GCM tag, SHA-256 của từng rule, tính hợp lệ của |
Create — Tạo |
|
Hai điểm vào:
Điểm vào | Là gì |
| CLI — dùng cho terminal, script, pipeline. |
| MCP server trên stdio — cho OpenCode, Claude Code, mọi MCP client khác. |
Công cụ này chỉ đọc và ghi file patch package. Nó không chạm vào container của app, không cần jailbreak, và không gọi lên máy chủ nào. Chính ứng dụng 3105 trên máy bạn mới là thứ áp patch. Xem mục 10.
Related MCP server: secret-vault-mcp
2. Yêu cầu & cài đặt
Cần | Chi tiết |
Node.js |
|
npm | đi kèm Node |
Không cần gì khác | không có native module, không gọi mạng, không cần công cụ của Apple |
2.1 Trên Termux
pkg update
pkg install nodejs-lts git
node -v # >= v18
npm -vĐảm bảo $PREFIX/bin nằm trong PATH (thường đã sẵn, nhưng thêm vào ~/.bashrc
cho chắc):
echo 'export PATH="$PREFIX/bin:$PATH"' >> ~/.bashrc
source ~/.bashrcDùng Node của Termux, đừng dùng bản Node cài từ Play Store / máy tính: global
npm prefix của Termux là $PREFIX/lib/node_modules và binary nằm ở $PREFIX/bin.
2.2 Cài từ git
npm i -g git+https://github.com/bbaobaob/3105-extract.git2.3 Cài từ tarball
Không cần git, không cần registry — hữu ích khi chuyển máy:
# trên máy đã có source
npm install
npm pack # -> 3105-extract-1.0.0.tgz
# trên máy nhận file .tgz
npm i -g ./3105-extract-1.0.0.tgz2.4 Kiểm tra đã cài được
3105x --helpMột dòng đầu tiên phải là:
3105x - inspect, unlock, extract, verify and create 3105 patch packagesKiểm tra nối MCP server (in dòng ready: ra stderr rồi thoát):
3105-mcp </dev/null
# [3105-mcp] ready: 3105-extract 1.0.0 on stdio (protocol 2024-11-05)Nếu
3105x: command not found, xem docs/INSTALL.md mục 7.
3. Bắt đầu nhanh
Các file mẫu nằm ở test/samples/ và tất cả dùng mật khẩu JFL. Ba lệnh bạn
dùng nhiều nhất:
cd /path/to/3105-extract
# 1. Xem thông tin — KHÔNG cần mật khẩu
3105x info test/samples/bIscreen.3105
# 2. Xem patch này sửa những gì
3105x list test/samples/bIscreen.3105 --password JFL
# 3. Giải mã ra thư mục để sửa tay
3105x extract test/samples/bIscreen.3105 --password JFL -o ./work/bIscreen1. info — không cần mật khẩu:
file test/samples/bIscreen.3105
packageID 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06
schemaVersion 9 (key AAD v9)
protected yes, PBKDF2-HMAC-SHA256 x250000
keyFingerprint feda3fcfef0c8d9d075582b28ffcd1d21cadf169be978baef1f9789a27a29ed8
fileSize 1738 bytes
(project is locked: pass --password to read it)2. list — xem toàn bộ rule:
9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06 (schema 9)
bundles com.zerone.hidesktop
appGroups group.hidesktop
RULES (2)
CONTAINER BUNDLE PATH OPERATION FILE SIZE SHA256
--------- -------------------- ---------------------------------------------- ---------- -------------------------- ---- ----------------
appGroup group.hidesktop Library/Preferences/group.hidesktop.plist mergePlist group.hidesktop.plist 247B 39461d6213c2141c
app com.zerone.hidesktop Library/Preferences/com.zerone.hidesktop.plist mergePlist com.zerone.hidesktop.plist 147B 71d2574ef2463934
DIRECTORIES (0)
(none)3. extract — giải mã ra workspace:
extracted bIscreen (9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06, schema 9) to ./work/bIscreen
3 files:
AppGroup/group.hidesktop/Library/Preferences/group.hidesktop.plist
Application/com.zerone.hidesktop/Library/Preferences/com.zerone.hidesktop.plist
manifest.jsonSau đó: sửa file trong ./work/bIscreen, pack lại, verify lại. Chi tiết ở
mục 4.
4. Chi tiết từng lệnh
Có 12 subcommand. Quy ước chung:
--jsoncó thể đặt ở bất kỳ vị trí nào trên dòng lệnh; nó in ra đúng một object JSON ra stdout, thay cho output dễ đọc.Mọi lỗi in ra stderr và exit code 1. Thành công là exit code 0.
3105x --helpin usage và exit 0; gọi3105xkhông có subcommand in usage và exit 1; subcommand lạ in usage kèm lỗi và exit 1.Có thể dùng
--flag=valuehoặc--flag value.-olà dạng ngắn của--output.3105x --versionin3105x on Node <version>(exit 0). Chỉ có tác dụng khi không kèm subcommand.Không có cờ
--deepvà không có-p. Mật khẩu chỉ nhận qua--password <P>. Dùng sai cờ sẽ báounknown flag.
4.1 info — metadata của envelope
Đọc phần công khai của gói. Không cần mật khẩu. Nếu gói không có mật khẩu, lệnh
in luôn cả metadata project; nếu có mật khẩu, thêm --password mới đọc được phần
đó.
Cú pháp
3105x info <file> [--password P] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Đường dẫn tới file |
| không | Mật khẩu, chỉ cần khi muốn đọc metadata project. |
| không | In một object JSON thay cho bảng text. |
Ví dụ 1 — không mật khẩu
$ 3105x info test/samples/bIscreen.3105
file test/samples/bIscreen.3105
packageID 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06
schemaVersion 9 (key AAD v9)
protected yes, PBKDF2-HMAC-SHA256 x250000
keyFingerprint feda3fcfef0c8d9d075582b28ffcd1d21cadf169be978baef1f9789a27a29ed8
fileSize 1738 bytes
(project is locked: pass --password to read it)Ví dụ 2 — có mật khẩu, thấy thêm metadata project
$ 3105x info test/samples/bIscreen.3105 --password JFL
file test/samples/bIscreen.3105
packageID 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06
schemaVersion 9 (key AAD v9)
protected yes, PBKDF2-HMAC-SHA256 x250000
keyFingerprint feda3fcfef0c8d9d075582b28ffcd1d21cadf169be978baef1f9789a27a29ed8
fileSize 1738 bytes
project bIscreen
author b
private true
createdAt 2026-09-07T09:07:16.337Z
updatedAt 2026-09-07T09:07:16.337Z
rules 2
directories 0
bundles com.zerone.hidesktop
appGroups group.hidesktop
settings 0 entriesLỗi thường gặp
3105x: cannot read nope.3105: ENOENT: no such file or directory, open 'nope.3105' [IO]
3105x: not a 3105 patch file (missing "3105PATCH\0" magic) [UNSUPPORTED_FORMAT]
3105x: authentication failed: wrong password or corrupted patch data [DECRYPT_FAILED]Trong thông báo thật,
\0ở trên là một byte NUL thật (byte00cuối magic). Ở đây ghi bằng\0cho dễ đọc.
4.2 list — toàn bộ rule, thư mục và bundle
Giải mã gói rồi liệt kê từng rule (bundle, loại container, đường dẫn, operation, kích thước, SHA-256) cùng danh sách bundle / App Group. Cần mật khẩu nếu gói có mật khẩu.
Cú pháp
3105x list <file> [--password P] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Đường dẫn tới file |
| tuỳ | Bắt buộc với gói có mật khẩu. |
| không | In một object JSON. |
Ví dụ 1 — dạng bảng
$ 3105x list test/samples/bWidgy.3105 --password JFL
8BD2B5BC-EC53-4192-A7EF-EB1DD33D2AC7 (schema 9)
bundles -
appGroups group.woodsign.widgy
RULES (1)
CONTAINER BUNDLE PATH OPERATION FILE SIZE SHA256
--------- -------------------- -------------------------------- --------- ------------------ ----- ----------------
appGroup group.woodsign.widgy widgyDefaults/tiny-storage.plist replace tiny-storage.plist 1088B 64bbb55b93719d73
DIRECTORIES (1)
group.woodsign.widgy/widgyDefaultsVí dụ 2 — dạng JSON
$ 3105x list test/samples/bIscreen.3105 --password JFL --json
{
"file": "test/samples/bIscreen.3105",
"packageID": "9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06",
"schemaVersion": 9,
"bundles": ["com.zerone.hidesktop"],
"appGroups": ["group.hidesktop"],
"directories": [],
"rules": [
{
"id": "F5046C99-F94C-48EB-99CA-DB15F5633999",
"bundleID": "group.hidesktop",
"containerKind": "appGroup",
"relativePath": "Library/Preferences/group.hidesktop.plist",
"operation": "mergePlist",
"replacementFilename": "group.hidesktop.plist",
"bytes": 247,
"sha256": "39461d6213c2141c0d352e94a29650604bbf5f61889bc970a573c78f6d38fe45"
}
]
}Ghi chú: containerKind là app (container của app, đọc ra là application) hoặc
appGroup. operation là replace, mergePlist hoặc mergeJson.
Lỗi thường gặp
3105x: this patch is password protected: a password is required [PASSWORD_REQUIRED]
3105x: authentication failed: wrong password or corrupted patch data [DECRYPT_FAILED]4.3 unlock — kiểm tra mật khẩu, lấy content key
Dẫn xuất content key AES-256 từ mật khẩu, in ra dạng hex, và so với keyFingerprint
trong envelope. Đây là cách rẻ nhất để biết mật khẩu đúng hay sai mà không phải
giải mã cả payload. fingerprint phải là MATCH, nếu không lệnh exit 1.
Cú pháp
3105x unlock <file> --password P [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Đường dẫn tới file |
| có | Không có cờ rút gọn. |
| không | In một object JSON. |
Ví dụ
$ 3105x unlock test/samples/bIscreen.3105 --password JFL
packageID 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06
schemaVersion 9
keyAADVersion 9
contentKey 3301d19e4724a1c54a13dbb392cee77c1699459b909888b05df4a1fe0718ef3e
keyFingerprint feda3fcfef0c8d9d075582b28ffcd1d21cadf169be978baef1f9789a27a29ed8
fingerprint MATCH
contentKeylà khoá bí mật của gói patch. Không in nó ra log, tin nhắn hay issue công khai. Chỉ dùngunlockđể kiểm tra rồi bỏ qua dòng đó.
Lỗi thường gặp
3105x: --password needs a value [USAGE]
3105x: authentication failed: wrong password or corrupted patch data [DECRYPT_FAILED]
fingerprint MISMATCH # exit 1 — sai mật khẩu hoặc package bị sửa4.4 extract — giải mã ra workspace
Giải mã toàn bộ gói và ghi ra thư mục có thể sửa tay. Thư mục đích được tạo nếu
chưa có; mọi thành phần đường dẫn đều được kiểm tra nên không thể ghi ra ngoài
-o.
Cú pháp
3105x extract <file> -o DIR [--password P] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Đường dẫn tới file |
| có | Thư mục đích. |
| tuỳ | Bắt buộc với gói có mật khẩu. |
| không | In một object JSON. |
Cấu trúc workspace:
work/bIscreen/
manifest.json # manifest trích xuất
settings.json # chỉ có khi project có settings
Application/com.zerone.hidesktop/Library/Preferences/com.zerone.hidesktop.plist
AppGroup/group.hidesktop/Library/Preferences/group.hidesktop.plistVí dụ 1 — giải nén một gói
$ 3105x extract test/samples/bIscreen.3105 --password JFL -o ./work/bIscreen
extracted bIscreen (9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06, schema 9) to ./work/bIscreen
3 files:
AppGroup/group.hidesktop/Library/Preferences/group.hidesktop.plist
Application/com.zerone.hidesktop/Library/Preferences/com.zerone.hidesktop.plist
manifest.jsonVí dụ 2 — dạng JSON
$ 3105x extract test/samples/bIscreen.3105 --password JFL -o ./work/x --json
{
"file": "test/samples/bIscreen.3105",
"outDir": "/data/data/com.termux/files/home/work/x",
"packageID": "9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06",
"schemaVersion": 9,
"written": [
"AppGroup/group.hidesktop/Library/Preferences/group.hidesktop.plist",
"Application/com.zerone.hidesktop/Library/Preferences/com.zerone.hidesktop.plist",
"manifest.json"
]
}Ghi chú: settings.json chỉ được ghi khi project có ít nhất một phần tử
settings — nên bIscreen (0 entries) chỉ ra 3 file ở trên.
Lỗi thường gặp
3105x: missing required option -o/--output [USAGE]
3105x: -o needs a value [USAGE]
3105x: missing required argument <file> [USAGE]4.5 pack — đóng gói workspace thành .3105
Đọc ngược một workspace rồi ghi ra file .3105. Giữ nguyên packageID, createdAt,
updatedAt, id của rule và operation khi có trong manifest.json; phần thiếu sẽ
tự sinh. Thư mục cha của file đích được tạo tự động.
Cú pháp
3105x pack DIR -o FILE [--password P] [--schema 3|9] [--name N] [--author A]
[--iterations N] [--private] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Workspace (output của |
| có | File |
| không | Đặt mật khẩu. Bỏ trống ⇒ gói không mật khẩu. |
| không | Ép |
| không | Ghi đè |
| không | Ghi đè |
| không | Số vòng PBKDF2. Mặc định |
| không | Đặt |
| không | In một object JSON. |
Ví dụ 1 — pack có mật khẩu, đổi tên
$ 3105x pack ./work/bIscreen -o ./out/bIscreen-patched.3105 --password JFL \
--name "bIscreen (mine)" --author "tac gia"
packed 2 rules from ./work/bIscreen
file ./out/bIscreen-patched.3105
packageID 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06
schemaVersion 9
protected yes
kdfIterations 250000
keyFingerprint 6d412976398530f6964c77506348855eb34ce77827ff034699f4f0c9057e201b
fileSize 1768 bytesVí dụ 2 — pack không mật khẩu, ép schema 9
$ 3105x pack ./work/bIscreen -o ./out/plain.3105 --schema 9
packed 2 rules from ./work/bIscreen
file ./out/plain.3105
packageID 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06
schemaVersion 9
protected no
kdfIterations -
keyFingerprint 9ad8a4ae830be35ef0d78316c3901b46803443283fa5e015235d311947a62b20
fileSize 1644 bytesSau khi pack, luôn verify lại (mục 4.6).
Lỗi thường gặp
3105x: cannot read workspace /tmp/nope: ENOENT: ... [WORKSPACE_NOT_FOUND]
3105x: --schema must be 3 or 9 [USAGE]
3105x: kdfIterations must be an integer between 100000 and 1000000, got 100 [VALIDATION]
3105x: manifest.json is not valid JSON: ... [VALIDATION]4.6 verify — kiểm tra toàn vẹn
Chạy toàn bộ kiểm tra: magic → envelope → keyFingerprint → tham số KDF → mở khoá
content key → so fingerprint → giải mã payload → parse plist → kiểm tra cấu trúc
project → so packageID → so SHA-256 của từng rule. In từng bước với [ok] hoặc
[!!]. ok khi tất cả pass, FAILED khi có bước fail — và exit 1.
Cú pháp
3105x verify <file> [--password P] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Đường dẫn tới file |
| tuỳ | Không có nó thì các bước sau |
| không | In |
Ví dụ 1 — verify thành công
$ 3105x verify test/samples/bIscreen.3105 --password JFL
OK test/samples/bIscreen.3105
[ok] magic: 3105PATCH\0 magic present
[ok] envelope: envelope parsed (schemaVersion 9, packageID 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06)
[ok] keyFingerprint: keyFingerprint is 32 bytes
[ok] kdfIterations: kdfIterations 250000 (allowed 100000..1000000)
[ok] kdfSalt: kdfSalt is 16 bytes
[ok] wrappedKey: wrappedContentKey is 60 bytes
[ok] keyUnwrap: content key unwrapped
[ok] contentKeyFingerprint: contentKey fingerprint e57818b33ac98cf4430d0100d369e924fa9f85d8351e1322cf14b9e68b8e695c
[ok] payloadDecrypt: payload decrypted (1301 bytes)
[ok] payloadPlist: payload is a binary plist dictionary
[ok] projectStructure: project bIscreen with 2 rule(s)
[ok] packageIDMatch: project id 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06 vs envelope 9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06
[ok] ruleDigests: 2 rule digest(s) matchVí dụ 2 — verify thiếu mật khẩu
$ 3105x verify test/samples/bIscreen.3105; echo "exit=$?"
FAILED test/samples/bIscreen.3105
[ok] magic: 3105PATCH\0 magic present
...
[ok] wrappedKey: wrappedContentKey is 60 bytes
[!!] keyUnwrap: password required for a protected package: rerun with --password
exit=1Lỗi thường gặp
Dòng | Nguyên nhân |
| Thiếu |
| Sai mật khẩu hoặc package bị sửa. |
| Sai mật khẩu, AAD sai, hoặc dữ liệu hỏng. |
| Payload bị sửa tay mà chưa |
4.7 read — đọc nội dung một rule
In replacementData của một rule mà không ghi gì ra đĩa. Nếu nội dung là UTF-8
hợp lệ thì in dạng text; ngược lại in base64(N bytes): kèm chuỗi base64.
Cú pháp
3105x read <file> <relativePath> [--password P] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Đường dẫn tới file |
| có | Đường dẫn như |
| tuỳ | Bắt buộc với gói có mật khẩu. |
| không | In một object JSON, kèm |
Nếu relativePath không khớp, tool thử lại bằng replacementFilename.
Ví dụ 1 — payload là text thì in thẳng nội dung
$ 3105x read test/samples/bPVZ.3105 "Documents/userdata/user1_json.dat" --password JFL
{"NewAchievementUnlcoked":false,"NoAdsPackActive":true,"NoAdsPackExpiry":0,"ReplayingLevel":-1,"SpeedBoostFTUDone":false…
...,"version":126,"zengardenTutorialComplete":false,"zombiesKilled":0}(Nội dung thật dài 69530 byte; ở trên chỉ cắt hai đầu cho dễ đọc.)
Ví dụ 2 — payload là bplist thì ra base64
$ 3105x read test/samples/bIscreen.3105 \
"Library/Preferences/group.hidesktop.plist" --password JFL
base64(247 bytes):
YnBsaXN0MDDSAQIDBFxIaVdpZGdldFZpcHNcZ3JvdXBfaGFzVmlwTxCneyJpc1RyeSI6ZmFsc2UsInByb2R1Y3RJRCI6...Muốn đọc nội dung plist cho dễ hiểu: 3105x read … --json rồi chuyển base64 sang file
rồi dùng 3105x convert (mục 4.12), hoặc đơn giản hơn là extract rồi convert.
Lỗi thường gặp
3105x: no rule with relativePath "nope.plist"; available: Library/Preferences/group.hidesktop.plist, Library/Preferences/com.zerone.hidesktop.plist [USAGE]
3105x: missing required argument <relativePathInPayload> [USAGE]4.8 settings — nội dung settings.json
In phần settings của project (mảng các trường có kiểu mà người dùng điền, dùng cho
placeholder {{…}}). Không có settings thì in [].
Cú pháp
3105x settings <file> --password P [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Đường dẫn tới file |
| có | Luôn cần để giải mã payload. |
| không | In |
Ví dụ
$ 3105x settings test/samples/bIscreen.3105 --password JFL
[]
$ 3105x settings test/samples/bIscreen.3105 --password JFL --json
{
"file": "test/samples/bIscreen.3105",
"packageID": "9D4CC6CD-CDB7-45CE-AB02-EACC1E87BA06",
"schemaVersion": 9,
"settings": []
}Lỗi thường gặp
3105x: this patch is password protected: a password is required [PASSWORD_REQUIRED]4.9 scan — tóm tắt cả thư mục
Liệt kê mọi *.3105 trong một thư mục (không đệ quy vào thư mục con) và in envelope
của từng cái. Không cần mật khẩu; nếu có --password thì cột NAME/PRIVATE
cũng được điền. File không đọc được hiện error thay vì làm hỏng cả lần quét.
Cú pháp
3105x scan <dir> [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Thư mục chứa file |
| không | Không cần để đọc envelope. Nếu có, |
| không | In |
Ví dụ 1 — không mật khẩu
$ 3105x scan test/samples
NAME FILE SCHEMA PRIVATE FINGERPRINT SIZE
---- ------------- ------ ------- ---------------- -------
- bFIMO.3105 3 - ab86caeeae9ef486 170460B
- bIscreen.3105 9 - feda3fcfef0c8d9d 1738B
- bPVZ.3105 3 - 5f8f9e04c95591d3 70615B
- bWidgy.3105 9 - c161e6b8e2a32b07 2266BVí dụ 2 — có mật khẩu thì NAME và PRIVATE được điền
$ 3105x scan test/samples --password JFL
NAME FILE SCHEMA PRIVATE FINGERPRINT SIZE
-------- ------------- ------ ------- ---------------- -------
b'FIMO bFIMO.3105 3 yes ab86caeeae9ef486 170460B
bIscreen bIscreen.3105 9 yes feda3fcfef0c8d9d 1738B
b'PVZ bPVZ.3105 3 yes 5f8f9e04c95591d3 70615B
b'Widgy bWidgy.3105 9 yes c161e6b8e2a32b07 2266BThư mục không có file .3105: in no *.3105 files in <dir> và exit 0.
Lỗi thường gặp
3105x: cannot read directory /tmp/nope: ENOENT: ... [IO]4.10 batch-extract — giải nén cả thư mục
Giải nén mọi *.3105 trong thư mục bằng một mật khẩu, mỗi gói ra một thư mục con
đặt theo tên file (bỏ đuôi .3105). Cái nào lỗi được ghi riêng; exit 1 nếu có
bất kỳ cái nào lỗi.
Cú pháp
3105x batch-extract <dir> -o DIR [--password P] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | Thư mục chứa file |
| có | Thư mục đích. |
| tuỳ | Bắt buộc nếu có gói có mật khẩu. |
| không | In |
Ví dụ
$ 3105x batch-extract test/samples -o ./work/batch --password JFL
extracted 4/4 patches to ./work/batch
bFIMO.3105 -> ./work/batch/bFIMO (2 files)
bIscreen.3105 -> ./work/batch/bIscreen (3 files)
bPVZ.3105 -> ./work/batch/bPVZ (2 files)
bWidgy.3105 -> ./work/batch/bWidgy (2 files)Trong thư mục trộn lẫn gói có mật khẩu và gói không mật khẩu với hai mật khẩu
khác nhau thì batch-extract không phù hợp — chạy extract cho từng gói.
Lỗi thường gặp
3105x: missing required option -o/--output [USAGE]
# và các dòng kiểu:
bPVZ.3105 -> FAILED: authentication failed: wrong password or corrupted patch data4.11 merge-preview — xem trước kết quả merge
Áp đúng ngữ nghĩa merge của 3105 cho một file: lấy replacementData của rule làm
payload, truyền cùng nội dung hiện tại của file đích (base64) vào merge engine, rồi
trả về byte đã merge (base64) + notes. Dry-run hoàn toàn, không ghi gì ra đĩa.
Cú pháp
3105x merge-preview <file> --password P --target <relativePath>
--content-base64 B64 [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | File |
| có | Giải mã payload. |
| có | Đường dẫn rule cần merge. |
| có | Nội dung hiện tại của file đích, mã hoá base64. |
| không | In một object JSON. |
Ví dụ
$ 3105x merge-preview test/samples/bIscreen.3105 --password JFL \
--target "Library/Preferences/com.zerone.hidesktop.plist" \
--content-base64 "$(printf '<plist/>' | base64 -w0)"
com.zerone.hidesktop/Library/Preferences/com.zerone.hidesktop.plist (mergePlist)
format plist
target 8 bytes
merged 385 bytes sha256 e75329878b62e3b7af52e03887124f0d8d1a7a27a4b43cb16859026de972bfd7
notes:
- target could not be parsed (<plist/> has no root value); merging into an empty target
- target Library/Preferences/com.zerone.hidesktop.plist is empty: the payload becomes the result
merged (base64):
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAi...notes cho biết merge engine làm gì: key mới được thêm, key trùng bị ghi đè, dict
lồng nhau gộp đệ quy, mảng và giá trị vô hướng bị thay trọn vẹn, và các directive
%3105_Remove% / %3105_Overwrite% / %3105_MatchPrefix%….
Lỗi thường gặp
3105x: missing required option --target [USAGE]
3105x: missing required option --content-base64 [USAGE]
3105x: --content-base64 must be non-empty base64 [USAGE]4.12 convert — chuyển đổi plist ⇄ JSON ⇄ XML
Chuyển một payload file độc lập (không phải file .3105) giữa binary plist,
XML plist và JSON. Đây là công cụ để nhìn nội dung plist mà không cần mở app.
Cú pháp
3105x convert <file> [--from auto|plist|json|xml] [--to json|xml|plist] [--json]Flag | Bắt buộc? | Ý nghĩa |
| có | File payload cần chuyển. |
| không | Mặc định |
| không | Mặc định |
| không | In kết quả dạng object JSON kèm |
Không phải file .3105, không cần mật khẩu.
Ví dụ 1 — binary plist ra XML
$ 3105x convert group.bplist --to xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>HiWidgetVips</key>
<data>eyJpc1RyeSI6ZmFsc2UsInByb2R1Y3RJRCI6ImNvbS56ZXJvbmUuaGlkZXNrdG9wLmZvcmV2ZXIzIiwi…</data>
<key>group_hasVip</key>
<true/>
</dict>
</plist>(<data> là một dòng dài, ở trên đã cắt ngắn. XML do convert sinh ra có key
đã sắp xếp, nên đầu ra ổn định — thuận tiện để diff.)
Ví dụ 2 — ra binary plist thì nhận base64
$ 3105x convert group.bplist --to plist --json
{
"file": "./group.bplist",
"from": "bplist",
"to": "plist",
"bytes": 247,
"sha256": "39461d6213c2141c0d352e94a29650604bbf5f61889bc970a573c78f6d38fe45",
"content": null,
"contentBase64": "YnBsaXN0MDDSAQIDBFxIaVdpZGdldFZpcHNcZ3JvdXBfaGFzVmlwTxCneyJpc1RyeSI6..."
}Không có --json thì convert in thẳng kết quả ra stdout, nên đổi định dạng bằng
shell:
3105x convert group.bplist --to xml > group.xml
3105x convert group.xml --to jsonLỗi thường gặp
3105x: --to must be plist, xml or json [USAGE]
3105x: input is neither a binary plist, an XML plist nor JSON [VALIDATION]5. MCP server cho AI agent
3105-mcp là một MCP server trên stdio (JSON-RPC 2.0, mỗi frame một dòng). Nhờ vậy
một AI agent (OpenCode, Claude Code, …) có thể tự gọi đúng công cụ thay vì tự viết code
mã hoá.
Server không có flag nào. Đừng truyền --help. Chạy 3105-mcp </dev/null để
kiểm tra nó khởi động được không.
5.1 Đăng ký vào OpenCode
Sửa ~/.config/opencode/opencode.json (thêm hoặc gộp vào khối mcp sẵn có):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"3105": {
"type": "local",
"command": [
"node",
"/data/data/com.termux/files/home/src/3105-extract/bin/3105-mcp.mjs"
],
"enabled": true
}
}
}Lưu ý:
commandlà mảng argv, không phải chuỗi shell — không cần quote, không dùng được&&.Nếu OpenCode chạy ngoài Termux shell của bạn,
nodephải resolve được theoPATHcủa tiến trình đó. An toàn nhất là dùng đường dẫn tuyệt đối:/data/data/com.termux/files/usr/bin/node.Sửa xong khởi động lại OpenCode (config chỉ đọc lúc startup).
Muốn dùng bản global cài bằng npm, trỏ thẳng vào binary:
"command": ["3105-mcp"]
Với Claude Code:
claude mcp add 3105 -- node /data/data/com.termux/files/home/src/3105-extract/bin/3105-mcp.mjs5.2 Danh sách 14 tools
Tool | Làm gì | Cần mật khẩu |
| Metadata envelope của một gói: magic, schema, packageID, cờ mật khẩu, fingerprint, kích thước, sha256. Trả cả metadata project nếu gói không có mật khẩu. | Không |
| Metadata envelope cho nhiều gói trong một lệnh (tối đa 200 path). File hỏng được báo | Không |
| Quét thư mục | Không |
| Dẫn xuất content key (hex) + kết quả so fingerprint. Dùng để xác nhận mật khẩu trước khi giải nén. | Có |
| Giải mã và liệt kê mọi rule: bundleID, containerKind, relativePath, operation, kích thước, sha256, cùng bundles/appGroups/settings và tổng. | Có |
| Nội dung | Có |
| Đọc một | Có |
| Giải mã toàn bộ workspace ( | Có |
| Giải nén mọi gói trong thư mục bằng một mật khẩu, một thư mục con mỗi gói, tách success/failure. | Có |
| Workspace → | Tuỳ chọn |
| Kiểm tra đầy đủ: magic, envelope, AAD/derivation, xác thực payload và mọi digest của rule. Trả từng bước kèm cờ ok. | Tuỳ chọn |
| Dry-run ngữ nghĩa merge 3105 cho một file; trả byte đã merge (base64), format và notes. Không ghi đĩa. | Có |
| Chuyển payload file: JSON ⇄ XML plist ⇄ binary plist. | Không |
| Cheat-sheet định dạng nhúng sẵn: magic, envelope, PBKDF2 250k/16B, AES-256-GCM, hai chuỗi AAD, cấu trúc payload, layout workspace. Không có tham số. | Không |
5.3 Khi nào nên để agent "chơi" (defer) với công cụ này
Nên gọi MCP tools khi:
người dùng nhắc tới
.3105, "patch 3105", "giải mã patch", "patch này sửa gì", "tạo patch 3105", "bplist patch package";cần biết một patch làm gì mà không muốn đọc tay binary plist;
cần giải nén hàng loạt gói trong một thư mục;
cần tạo patch mới rồi kiểm chứng nó đọc lại được;
cần xem nội dung plist/JSON của một payload cho dễ đọc.
Nên defer (bảo agent tự làm / hỏi lại) khi:
người dùng hỏi về app 3105 nhưng không phải về file
.3105;file không có magic
3105PATCH\0— đó không phải patch 3105, đừng cố;người dùng muốn áp patch lên container của app của người khác hoặc thiết bị không phải của họ;
chưa có mật khẩu — hãy hỏi, đừng đoán;
cần chỉnh sửa byte thô của file
.3105— hãy dùngextract→ sửa →pack.
6. Tab completion
3105x không tự bật completion. Cách nhanh nhất gõ u rồi Tab để ra unlock:
$ 3105x u<Tab>
unlock
$ 3105x <Tab>
batch-extract convert extract info list merge-preview pack read scan settings unlock verify6.1 Cách dùng
File completion nằm ở completions/3105x.bash trong repo, và có được đóng gói
kèm vào tarball khi npm pack. Source nó từ ~/.bashrc:
echo 'source /data/data/com.termux/files/home/src/3105-extract/completions/3105x.bash' >> ~/.bashrc
source ~/.bashrcNếu bạn cài global bằng npm và không giữ checkout source, tìm file đó trong bản cài:
find "$(npm root -g)/3105-extract" -name '3105x.bash'
# $(npm root -g)/3105-extract/completions/3105x.bashSau khi source, kiểm tra:
complete -p 3105x
# complete -F _3105x 3105xCơ chế: 3105x u<Tab> → unlock; 3105x pack --<Tab> → danh sách flag hợp lệ của
đúng subcommand đó (scan không có --password, pack có đủ 8 flag); đối số file
thì hoàn thành theo filesystem, và scan / batch-extract / pack hoàn thành theo
thư mục.
6.2 Biết trước
Bản completions/3105x.bash hiện tại gợi ý thêm hai thứ mà CLI không chấp nhận:
helpnhư một subcommand (CLI chỉ có--help).--outchoextract/pack/batch-extract(CLI chỉ nhận--output, viết tắt là-o).
Gõ sai sẽ báo unknown flag / unknown command và exit 1 — vô hại, nhưng đừng lấy
các gợi ý đó làm chuẩn. Danh sách cờ đúng nằm ở mục 4.
Nếu bản cài của bạn không có thư mục completions/, đây là một đoạn tương đương đã
kiểm thử, bạn có thể dán thẳng vào ~/.bashrc:
_3105x() {
local cur prev sub flags
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Các flag có giá trị: hoàn thành bằng đường dẫn.
case "$prev" in
-o|--output|--password|--schema|--name|--author|--iterations|--target|--content-base64|--from|--to)
COMPREPLY=( $(compgen -f -- "$cur") ); return 0 ;;
esac
sub="${COMP_WORDS[1]}"
case "$sub" in
unlock|settings|info|list|extract|verify|read) flags="--password --json --help" ;;
pack) flags="-o --output --password --schema --name --author --iterations --private --json --help" ;;
batch-extract) flags="-o --output --password --json --help" ;;
merge-preview) flags="--password --target --content-base64 --json --help" ;;
convert) flags="--from --to --json --help" ;;
scan) flags="--json --help" ;;
*) flags="--json --help --version" ;;
esac
# Đang gõ một flag: chỉ gợi ý các flag hợp lệ cho subcommand đó.
if [[ "$cur" == -* ]]; then
COMPREPLY=( $(compgen -W "$flags" -- "$cur") ); return 0
fi
# Chưa chọn subcommand: gợi ý 12 subcommand.
if [[ "$COMP_CWORD" -eq 1 ]]; then
COMPREPLY=( $(compgen -W "info list unlock extract pack verify read settings scan batch-extract merge-preview convert" -- "$cur") )
return 0
fi
# Đã chọn subcommand: thư mục hay file.
case "$sub" in
scan|batch-extract|pack) COMPREPLY=( $(compgen -d -- "$cur") ) ;;
*) COMPREPLY=( $(compgen -f -- "$cur") ) ;;
esac
return 0
}
complete -F _3105x 3105x7. Scripts hữu ích
install-skills.sh — cài 2 skill cho OpenCode
Script này chép (không chạy) hai skill OpenCode vào ~/.config/opencode/skills/:
Skill | Dùng khi nào |
| Xem / mở khóa / giải nén / kiểm tra một patch có sẵn. |
| Tự tạo patch mới và kiểm tra trước khi chia sẻ. |
Cách dùng:
cd /path/to/3105-extract
./install-skills.sh # cài / làm mới cả hai skill
./install-skills.sh --dest DIR # cài vào thư mục khác
./install-skills.sh --remove # gỡ ra
./install-skills.sh --help # in hướng dẫnMặc định đích là $HOME/.config/opencode/skills, hoặc $OPENCODE_SKILLS_DIR nếu bạn
đặt biến môi trường đó. Sau khi cài, khởi động lại OpenCode để nó nạp skill.
Làm thủ công (tương đương):
mkdir -p ~/.config/opencode/skills
cp -r skills/3105-patch ~/.config/opencode/skills/
cp -r skills/3105-patch-author ~/.config/opencode/skills/8. Định dạng file
Tóm tắt ba ý, chi tiết byte-level ở docs/FORMAT.md:
Magic — 10 byte đầu: ASCII
3105PATCH+ byte NUL (62 33 31 30 35 50 41 54 43 48 00). Nằm ngoài plist, phải kiểm tra trước khi parse.Envelope — binary property list (
bplist00) chứaschemaVersion,keyAADVersion,packageID,isPasswordProtected,kdfSalt,kdfIterations,wrappedContentKey/publicContentKey,keyFingerprint,encryptedPayload. Đọc được không cần mật khẩu.Payload —
AES-256-GCMvớicontentKeysinh từPBKDF2-HMAC-SHA256(password, kdfSalt, 250000, 32). Payload là một binary plist:{ project: { … }, replacementDigests: { <rule id>: data(32) } }.
Sơ đồ pipeline mã hoá (đọc ngược chiều khi giải mã):
password (UTF-8)
│
PBKDF2-HMAC-SHA256(password, kdfSalt, kdfIterations, dkLen=32)
│
wrappingKey (32B)
│
AES-256-GCM open(wrappedContentKey, wrappingKey, "3105PATCH/v{n}/key/{packageID}")
│
contentKey (32B)
│ ← so SHA-256(contentKey) với keyFingerprint (ngoài mã hoá)
│
AES-256-GCM open(encryptedPayload, contentKey, "3105PATCH/v{n}/payload/{packageID}")
│
payload (binary plist) → project + replacementDigestsHai chuỗi AAD phải khớp từng byte; keyAADVersion (mặc định bằng
schemaVersion) quyết định phiên bản của chuỗi AAD phía key.
Áp dụng patch — app làm gì với từng rule:
| Tác dụng |
| Ghi đè toàn bộ file tại |
| Gộp dict đệ quy vào plist đích. |
| Gộp như trên với JSON; mảng và giá trị vô hướng bị thay trọn vẹn, không gộp. |
Thêm các directive trong payload: %3105_Remove%, %3105_Overwrite%,
%3105_MatchPrefix%<prefix>, và placeholder {{field}} lấy từ settings.json.
9. Ghi chú về schemaVersion
Có hai hệ schema, nhưng cùng một envelope:
v1 – v3 — có trong mã nguồn công khai (github.com/YangJiiii/3105, tag v1.0.0 … v2.0):
latestSchemaVersion = 3,minimumSchemaVersion = 1. v2 thêmkeyAADVersion+directories/bundleIdentifiers; v3 thêmauthor+isPrivate(private ⇒ cần mật khẩu ⇒ từ v3 trở lên). Magic, cách serialize và hệ mã hoá giống hệt nhau suốt v1–v3.v4 – v9 — chỉ có trong binary app 2.0 đã phát hành: các trường
settings, ánh xạ App Group,containerKind,operation. Mã nguồn công khai chưa có. Envelope không đổi một byte nào: cùng magic, cùng khoá envelope, cùng PBKDF2/AES-GCM, cùng cơ chế AAD. Gói thật từ app đó mangschemaVersion = 9vàkeyAADVersion = 9.
Tool này hiểu payload v3 và v9, và chuẩn hoá chúng về một model thống nhất khi đọc
nên extract → sửa → pack không mất thông tin.
Chọn schema khi pack:
Tình huống | Nên dùng |
Chỉ |
|
Cần |
|
Không chắc | Bỏ trống |
Một ứng dụng cũ hơn gói nó mở sẽ từ chối ở cổng envelope (unsupportedVersion).
Nếu gói của bạn không thật sự cần trường riêng của v9, hãy xuất --schema 3.
10. An toàn
Chỉ dùng với thiết bị và dữ liệu của chính bạn. Công cụ này đọc và ghi file patch package; nó không chạm vào container của app, không cần jailbreak và không gọi lên máy chủ nào của Apple. Chính ứng dụng 3105 trên máy bạn mới là thứ áp patch vào container.
Sao lưu trước khi áp patch. Áp patch ghi đè dữ liệu trong container của app. Export dữ liệu gốc ra một bản sao trước đã.
Mật khẩu không được lưu ở đâu.
3105-extractkhông có config, không có keyring, không có cache: mật khẩu chỉ tồn tại trong bộ nhớ của tiến trình và biến mất khi tiến trình kết thúc. Nhưng nó có xuất hiện trong:lịch sử shell — vì nằm trên dòng lệnh. Xoá bằng
history -d <số>hoặchistory -c, và tránhpslộ tham số khi chạy dài.log của công cụ khác (bao gồm transcript của AI agent) — đừng bảo agent in ra mật khẩu.
MCP: mỗi tool call tự mang tham số
passwordcủa riêng nó, không có mật khẩu mặc định toàn cục, và không có nơi nào lưu lại.
unlockin ra content key. Đó là khoá bí mật của gói patch. Dùng nó để kiểm tra rồi bỏ qua; không lưu, không chia sẻ.Xem
.3105của người khác là input không tin cậy. Nó là một plist sẽ được ghi vào thư mục dữ liệu của một app, với đường dẫn vàbundleIDdo người khác chọn.verifytrước khi chia sẻ bất kỳ gói nào bạn tự build.Chỉ có 2 dependency (
bplist-parser,bplist-creator), không có native module, không có telemetry, không có network call.
11. Đóng góp & phát triển
Chạy test
npm install
npm testBộ test giải mã package .3105 thật. Các file mẫu là riêng tư và không được
commit (.gitignore loại test/samples/*.3105), nên hãy bỏ file của bạn vào
test/samples/ — bất kỳ tên nào kết thúc bằng .3105 — và đặt mật khẩu trong
test/run-tests.mjs. Không có mẫu thì bộ test báo skip sạch và vẫn xanh:
SKIP no .3105 samples in test/samples (private, not committed)Cấu trúc thư mục
3105-extract/
├── bin/
│ ├── 3105x.mjs # CLI entry point
│ └── 3105-mcp.mjs # MCP stdio server (JSON-RPC 2.0 thuần, không SDK)
├── src/
│ ├── cli.mjs # phân tích argv + 12 lệnh + output text/JSON
│ ├── format.mjs # magic, parse/build envelope binary plist
│ ├── crypto.mjs # PBKDF2, AES-256-GCM, unwrap content key, fingerprint
│ ├── model.mjs # validate / normalize / denormalize project v3 <-> v9
│ ├── pack.mjs # inspect, decodePatch, encodePatch, verifyPatch
│ ├── workspace.mjs # đọc & ghi workspace (safeJoin chống path traversal)
│ ├── merge.mjs # ngữ nghĩa merge 3105 + chuyển đổi plist/JSON/XML
│ └── mcp/tools.mjs # định nghĩa 14 MCP tool + FORMAT_GUIDE
├── docs/
│ ├── FORMAT.md # đặc tả định dạng ở mức byte
│ └── INSTALL.md # cài đặt sâu, MCP, troubleshooting
├── skills/
│ ├── 3105-patch/ # skill OpenCode: xem / giải nén / kiểm tra
│ └── 3105-patch-author/ # skill OpenCode: tạo patch mới
├── completions/3105x.bash # bash completion cho 3105x
├── test/
│ ├── run-tests.mjs
│ └── samples/*.3105 # riêng tư, không commit
├── install-skills.sh
├── package.json # ESM, Node >= 18, bin: 3105x + 3105-mcp
└── README.mdTài liệu khác
Tài liệu | Nội dung |
Đặc tả byte-level: khoá envelope, PBKDF2/AES-GCM + chuỗi AAD, schema project v3/v9, layout workspace, ngữ nghĩa merge, ví dụ giải mã một gói v9 thật, công thức ghi file, bẫy thường gặp. | |
Cài đặt sâu: Termux, 3 cách cài, smoke test, đăng ký MCP, kiểm soát | |
Skill OpenCode cho luồng xem / giải nén / kiểm tra. | |
Skill OpenCode cho luồng tạo và kiểm chứng patch. |
Giấy phép
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Create, inspect and extract zip archives offline, with traversal, symlink and zip-bomb guards.
Production-grade cryptography toolkit with 31 MCP tools for classical, PQC, and KMS workflows.
Securely control computers you explicitly pair through files, terminals, processes, screenshots, desktop UI/input, clipboard, browser automation, diagnostics, and document tools.
Secure P2P File Transfer, Encrypted Chat & Communication | Decentralized P2P & AES-256-GCM encryption | Zero cloud logs. Zero registration. For humans and autonomous AI agents / MCP servers.
Related MCP Servers
- AlicenseBqualityDmaintenanceAgent-native SSH control plane with a local Web Terminal, human-in-the-loop secret input, keychain-backed profiles, and user-confirmed uploads for Codex, Claude Code, and MCP-compatible coding agents.181Apache 2.0
- AlicenseNot gradedqualityCmaintenanceLocal, encrypted password vault with AES-256-GCM and PBKDF2, exposing MCP tools for secure credential management, password generation, and search.MIT
- AlicenseAqualityCmaintenanceFree, fully-offline MCP utility tools for agents: base64, UUID, JSON validation, SHA hashing, timestamps, URL codec, case conversion, and secure passwords. No API keys, no tracking, works offline - stdlib-only Python stdio server.9MIT
- AlicenseNot gradedqualityBmaintenanceExposes a living systems memory vault to autonomous agents, letting them search notes with BM25 ranking, read sections within token budgets, extract API/port/schema contracts, compute dependency blast radius, and run machine-verifiable invariant assertions before mutating code. It also supports surgical frontmatter edits, schema linting, wikilink integrity checks, and worklog recording, over stdio MCP.MIT