v8unpack-cf — read and write 1C binary artifacts
Two workflows live here, and they are not variants of one thing:
| You have | Route | Section |
|---|---|---|
One ordinary form — .../Forms/<Форма>/Ext/Form.bin |
MCP tools unpack_ordinary_form / build_ordinary_form |
Ordinary forms — Form.bin |
| A whole CF / CFE / EPF binary | v8unpack CLI (-E / -B) |
Whole binaries — CF / CFE / EPF |
Use either when the 1C:Enterprise platform is not at hand. When the configuration
lives in an infobase, extract it through the platform instead — see the
getconfigfiles rule.
Dependency
v8unpackPython package —pip install "v8unpack>=1.2.6,<2". Verify withpython -m v8unpack --help.- Both MCP servers declare it in their own requirements, so the tools work in a
built image. When it is missing they answer
status="error",error_code="dependency_missing"with the install line inhint— that is the answer, not a crash.
Ordinary forms — Form.bin
An ordinary form is not XML. In a Designer file export a managed form writes
Forms/<Имя>/Ext/Form.xml; an ordinary form writes Forms/<Имя>/Ext/Form.bin — a
1C container holding two payload entries: form (the layout, a brace tree) and
module (the form module, BSL). The sidecar Forms/<Имя>.xml declares which model
it is, via <FormType>Ordinary</FormType>.
Find them with **/Forms/*/Ext/Form.bin. A form whose Form.xml appears to be
"missing" is almost always an ordinary form, not a broken export.
The MCP contract — ordinary-form/1
The same two tools, with the same arguments and the same result payload, are
published by 1c-code-metadata-mcp and 1c-graph-metadata-mcp. Learn them
once. On the graph server the payload arrives in the envelope's data section; on
the code server it is the result itself.
unpack_ordinary_form
| Parameter | Default | Meaning |
|---|---|---|
form_path |
— | The Form.bin to read. Absolute path; Cyrillic is normal. |
workspace_path |
— | Where to materialise the workspace. New, empty, or a workspace this tool wrote. |
overwrite |
false |
Replace an existing workspace of this contract. A non-empty directory the tool did not write is refused whatever this says. |
include |
"summary" |
Which bounded preview to embed: summary (paths only), structure, module, all. |
max_chars |
4000 |
Bound on each embedded preview (server cap 20 000). |
Result: status, contract, tool, workspace_path, manifest_path, source
(path / size / sha256), container, entries, entries_truncated, notes,
warnings — plus structure / structure_truncated and module_text /
module_truncated when include asks for them. Each entry carries name, kind
(brace_tree / bsl_module / text / container / binary), size, sha256,
encoding, deflated, path and decoded_path.
build_ordinary_form
| Parameter | Default | Meaning |
|---|---|---|
workspace_path |
— | A workspace written by unpack_ordinary_form. |
output_path |
— | The Form.bin to write. Never replaced silently. |
overwrite |
false |
Replace an existing output file. |
verify |
true |
Re-read the result and compare the payload. Leave it on. |
Result: status, contract, tool, workspace_path, output_path, output
(path / size / sha256), source, binary_identical, verified,
verification (match / mismatch / skipped, with per-entry hashes),
changed_entries, notes.
Errors
A refusal is status="error" with an error_code and an actionable hint. The
codes: dependency_missing, path_invalid, path_not_found, not_a_file,
not_a_container, unsupported_container_layout, workspace_not_empty,
workspace_exists, workspace_invalid, manifest_missing, manifest_invalid,
payload_missing, derived_edit_detected, output_exists, unpack_failed,
build_failed, verify_failed.
The workspace
<workspace>/
ordinary-form.json # manifest: source, entries, hashes. Generated — never hand-edit.
payload/
form # the layout brace tree, UTF-8 BOM + CRLF <- SOURCE OF TRUTH
module # the form module, BSL, UTF-8 BOM + CRLF <- SOURCE OF TRUTH
decoded/
form.json # the layout as JSON-compatible data <- derived, read-only
module.bsl # the module, for BSL tooling <- derived, read-only
Edit payload/. decoded/ is regenerated on every unpack and is never read
back. Editing a derived view and then building is refused with
derived_edit_detected rather than silently dropped — but only that refusal saves
the work, so do not rely on noticing.
Workflow — edit an ordinary form
# 1. Read it
unpack_ordinary_form(
form_path="C:\src\Catalogs\Товары\Forms\Форма\Ext\Form.bin",
workspace_path="C:\work\Форма")
# 2. Edit C:\work\Форма\payload\module (BSL) or payload\form (the layout tree).
# Keep the UTF-8 BOM and the CRLF line endings.
# 3. Write it back, and prove nothing was lost
build_ordinary_form(
workspace_path="C:\work\Форма",
output_path="C:\src\Catalogs\Товары\Forms\Форма\Ext\Form.bin",
overwrite=$true)
Check verification.status == "match". Then load the export into the infobase the
normal way (/update1cbase) — this skill writes a file, it does not touch an
infobase.
Paths with spaces or Cyrillic go in double quotes, always. In PowerShell prefer
single-quoted literals ('C:\src\...\Form.bin') when the path contains $.
Warnings — read before the first call
Form.binis a binary 1C container, not a document. Never edit it directly, never read it as XML, never open it with an XML tool. A text editor corrupts it silently. It is one container: the reference ordinary form holds exactly two entries,formandmodule, stored verbatim. A file holding more than one container is a whole CF / CFE / EPF and is refused withunsupported_container_layout; an entry that is itself a container is kept whole rather than descended into, which is what keeps the round trip lossless.- Do not judge a round trip by the final SHA-256.
v8unpackstamps container records with the current time, so a rebuiltForm.bindiffers byte for byte from its source even when nothing changed.binary_identical: falsetogether withverification.status: "match"is the normal, correct outcome. The equality that means "nothing was lost" is the logical payload — entry names, sizes and SHA-256 — which is whatverificationreports. - Preserve the workspace manifest and
payload/. They are what a build reads. A workspace withoutordinary-form.jsonis not a workspace (manifest_missing), and a missing payload entry is refused (payload_missing) rather than built around. - A standalone
Form.binnames nothing. Its brace tree is positional: it is structurally parseable, and no token in it says "this is a button called X". Do not invent element names, types or handlers from it. Named element trees (*.elem.json) come from high-level CF / CFE / EPF extraction, where the surrounding metadata supplies the names — and even there v8unpack 1.2.6 writes a populated tree only for managed forms (see Limitations). - On Windows use
--processes 1for the CLI unless parallel processing has been validated for the artifact at hand. The MCP tools spawn no processes at all. - Nothing is overwritten silently. An existing workspace needs
overwrite=true; an existing output file needsoverwrite=true; a non-empty directory the tool did not write is refused outright,overwriteor not.
Which route for a form
| Situation | Route |
|---|---|
| One ordinary form, sources already on disk | unpack_ordinary_form / build_ordinary_form |
Every form of a .cf / .cfe / .epf at once |
CLI -E, then the per-form files it writes |
| The infobase is available and you want XML sources | /getconfigfiles — not this skill |
A managed form (Ext/Form.xml) |
1c-metadata-manage skill — not this skill |
Whole binaries — CF / CFE / EPF
CLI commands
Extract (-E)
python -m v8unpack -E "<file.cf>" "<sources_dir>" --temp "<temp_dir>"
| Parameter | Description |
|---|---|
<file.cf> |
Path to a CF, CFE or EPF file |
<sources_dir> |
Destination for the unpacked sources (created automatically) |
--temp <path> |
Folder for intermediate data (kept, not deleted — useful for debugging) |
--processes N |
Number of worker processes (default: cpu_count - 2) |
--descent XYYZZZ |
Extension versioning mode (configuration version suffix) |
--auto_include |
Build the table of contents dynamically from the folder, not from the header |
--prefix STR |
Prefix for first-level metadata names |
Build (-B)
python -m v8unpack -B "<sources_dir>" "<file.cf>"
| Parameter | Description |
|---|---|
<sources_dir> |
Folder with the unpacked sources |
<file.cf> |
Path to the output CF / CFE / EPF file |
--index <path> |
JSON table-of-contents file (maps files across folders) |
--version XYYZZ |
Compatibility-mode version (for extensions), e.g. 80306 = 8.3.6 |
--descent XYYZZZ |
Configuration version suffix |
Index (-I)
python -m v8unpack -I "<sources_dir>" --index index.json --core core
Generates / updates index.json — the table-of-contents file that controls how sources
are laid out across subfolders.
Batch operations (-EA, -BA, -IA)
python -m v8unpack -EA products.json # extract all products
python -m v8unpack -BA products.json # build all products
python -m v8unpack -BA products.json --index KEY # build a specific product
products.json describes several products with their individual build parameters.
Python API
import v8unpack
v8unpack.extract('d:/sample.cf', 'd:/src')
v8unpack.extract('d:/sample.cf', 'd:/src', temp_dir='d:/temp',
options={'descent': 4100200, 'auto_include': True})
v8unpack.build('d:/src', 'd:/repacked.cf')
v8unpack.build('d:/src', 'd:/repacked.cf', index='index.json',
options={'descent': 4100200, 'version': '80306'})
Examples
Extract a configuration
python -m v8unpack -E "<project>/1Cv8.cf" "<project>/src" --temp "<project>/temp"
Build it back
python -m v8unpack -B "<project>/src" "<project>/1Cv8_new.cf"
Extract an external data processor
python -m v8unpack -E "MyDataProcessor.epf" "src_epf"
Extract an extension
python -m v8unpack -E "MyExtension.cfe" "src_cfe" --descent 3000112
Build an extension
python -m v8unpack -B "src_cfe" "bin/ext.cfe" --index cmd/index.json --descent 3000112 --version 80316
Version compatibility
The utility version is recorded in Configuration.json ("v8unpack": "1.2.6"). On
build, major.minor must match. If the versions differ:
- Build with the old version.
- Upgrade the utility.
- Re-extract with the new version.
- Commit.
Intermediate stages (--temp)
| Stage | Description |
|---|---|
decode_stage_0/ |
Extraction from the 1C container |
decode_stage_1/ |
Decompression (zlib), bracket-files |
decode_stage_3/ |
Metadata parsing → tree |
| Destination folder | Code organization (include, form elements) |
Limitations
Of the CLI route:
- Object properties and form layout are stored in
header/rawas raw arrays. - Files larger than 1 MB (layouts, HTML) are stored as
.binwithout decoding. - Encrypted modules are kept in binary form.
- With
--auto_include, nested objects are sorted alphabetically.
Of ordinary forms specifically — measured on a real extraction (1C-Gitter
1.1.0.8, 170 form artifacts, v8unpack 1.2.6), not assumed:
*.elem.jsonis populated for managed forms and empty for ordinary ones. 23 of 23 managed forms (Тип формы=1) came back with a namedtree; 147 of 147 ordinary forms (Тип формы=0, element version''or0-5-1) came back empty, because neither version is in v8unpack'sFormCore.supported_form_versions(0-26,0-27,1). So for the ordinary forms of a modern configuration the CLI route yields no named elements, and the layout has to be read fromForm.bin— positionally.v8unpack1.2.6 cannot write a container with compressed entries throughContainer.build(nested=False)— it raisesstruct.error, becauseDocument.compressreturns no table-of-contents offset. The MCP codec sidesteps this: it deflates an entry itself when the source stored it deflated, and writes the container with verbatim entries. Nothing to do at the call site; relevant only if you drive the library directly.- A rebuilt binary's SHA-256 always differs from its source (write timestamps). See the warning above.
Relationship to other rules
getconfigfiles— extracts configuration objects from a running infobase through the platform. Prefer it when an infobase is available; usev8unpack-cfwhen you only have a binary artifact and no platform.1c-metadata-manage— MCP-based skill for operating on the metadata structure once the sources are unpacked. It owns managed forms (Ext/Form.xml); ordinary forms are this skill's, because there is no XML for it to edit.forms.md— the router for managed-form work. It sends ordinary forms here.mcp-1c-tools— the MCP catalog.unpack_ordinary_form/build_ordinary_formare listed there under both1c-code-metadata-mcpand1c-graph-metadata-mcp; this file is the contract they share.