golded-ftn-jam
Repository: golded-ftn-jam-python.
The distribution remains golded-ftn-jam; imports use golded_ftn_jam.
The source is public on GitHub. Version 1.2.0 is available on PyPI.
Install with Python 3.12 or newer:
python -m pip install golded-ftn-jam==1.2.0
Read and edit JAM revision 1 areas through the golded-ftn models. Python 3.12+.
For development, clone core and the format package:
git clone https://github.com/golded-dev/golded-ftn-python.git
git clone https://github.com/golded-dev/golded-ftn-jam-python.git
cd golded-ftn-jam-python
uv sync --locked
from pathlib import Path
from tempfile import TemporaryDirectory
from golded_ftn import MessageBaseReader, ReaderOptions
from golded_ftn_jam import JamReader
# A minimal empty area. Real callers pass the area basename, without an extension.
with TemporaryDirectory() as directory:
base = Path(directory) / "example"
header = bytearray(1024)
header[:4] = b"JAM\0"
header[20:24] = (1).to_bytes(4, "little")
base.with_suffix(".JHR").write_bytes(header)
base.with_suffix(".JDT").write_bytes(b"")
base.with_suffix(".JDX").write_bytes(b"")
reader: MessageBaseReader = JamReader()
messages = list(reader.read(base, ReaderOptions(fallback_charset="CP850")))
assert messages == []
The basename is exact; .JHR, .JDT and .JDX extensions are matched without
regard to case. Each must identify one regular file; symlinks and ambiguous
extension variants are rejected. The standalone reader ignores .JLR; editing
preserves it and creation initializes an empty file.
The index determines message numbers and which headers are live. Index holes and
deleted messages are skipped. Old unindexed headers are ignored. The reader
validates signatures, revision, record boundaries, offsets, subfield lengths,
message numbers and reused header offsets. It reads the whole area into memory
and validates all indexed records before returning a tuple. A malformed later
record cannot leave a caller with a partial result. Filesystem errors remain
filesystem errors; damaged data and decoding errors raise ParserException with
source path, byte offset and a chained cause.
The standalone JamReader requires a stable area with no concurrent writes. It
does not lock the area or produce a snapshot across the three files. Stop the
writer or use a consistent copy before reading.
Decoding is strict, with core charset detection and CP850 fallback. Header FTSKLUDGE declarations are considered before body declarations. Conflicting charset declarations and IDs fail. Unknown charset names use the configured fallback. Mojibake repair is the caller's choice.
body_text contains only normalized .JDT text. Header control and routing
subfields go into control_lines, ahead of body metadata. Repeated controls and
routing entries keep their order within the core model's kludges, seen_by and
path sequences. Unknown subfields and nonzero HiID fields are bounds-checked
and skipped. The first originating/destination address is preserved as decoded
text, including node 0, point and domain. Other single-value subfields must agree
when repeated. Header MSGID/REPLYID take precedence over matching body IDs;
missing MSGID uses the core synthetic ID over decoded, normalized fields.
Dates are naive 1970-01-01 + DateWritten seconds, independent of the machine's
timezone; zero means None. TZUTC is retained as a control line. Raw attributes,
all three reply links and .JHR provenance are retained; zero reply links become
None. Unknown area metadata stays None.
Compressed, encrypted and escaped active messages are unsupported. Area discovery, databases, packing and repair are outside this package.
Development
uv sync --locked --python 3.14
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run python -m mypy.stubtest golded_ftn_jam
uv build
uv run twine check dist/*
uv run python scripts/verify_distribution.py
uv uses the sibling ../golded-ftn-python checkout during development. Published metadata
contains only golded-ftn>=1.2.0,<2. The sdist build hook removes tool.uv.sources
from the packed pyproject.toml; the development lock is also excluded.
Unpacked sources use the public dependency constraint.
See CONTRIBUTING.md and release checks.
Format references and credits
- JAM-001 revision 1
- GoldED JAM structures
- The sibling
laravel-ftn-jam/src/JamReader.phpand its reader tests informed model mapping. Its sequential scan and permissive truncation are not used.
JAM(mbp) - Copyright 1993 Joaquim Homrighausen, Andrew Milner, Mats Birch, Mats Wallin. ALL RIGHTS RESERVED.
The package code is MIT licensed; the JAM specification has its own terms.
Archive mode
Strict reading remains the default. Archive mode requires a report callback:
from golded_ftn import ReaderIssue, ReaderOptions
issues: list[ReaderIssue] = []
options = ReaderOptions(archive_mode=True, on_issue=issues.append)
# Pass options to JamReader().read(source, options).
Issues carry recovered, skipped or stopped, the actual filename, record
identity and physical offset. Their detail contains no message contents. A stop
means the traversal is incomplete; a validated prefix may still be returned.
Multiple issues can describe one record, including recovery followed by a skip.
Filesystem errors and callback exceptions propagate. Files must remain stable.
Bounded fields exceeding JAM's specification limits are retained and reported. Malformed TZUTC metadata is retained without interpretation. Distinct PID, FLAGS and TZUTC subfields stay in source order. Conflicting names, subjects, IDs or charset declarations are skipped rather than guessed. Failed records are skipped using the next fixed index slot; reused header offsets stop traversal.
If declared ASCII cannot decode a payload, the configured fallback is tried strictly and reported. The original charset control stays unchanged. Other decoding failures are skipped; there is no lossy decoding or mojibake repair.
Writing
JamWriter.create(base) creates .JHR, .JDT, .JDX and an empty .JLR.
Existing files are refused. The initial message number is 1.
from golded_ftn import MessagePatch, OutgoingMessage
from golded_ftn_jam import JamWriter
writer = JamWriter()
writer.create("new-area")
with writer.open("new-area") as session:
result = session.append(
OutgoingMessage(
from_name="Alice",
to_name="Bob",
subject="Hello",
body_text="Hello Bob",
)
)
result = session.update(
result.identity, MessagePatch(subject="Changed"), result.revision
)
session.delete(result.identity, result.revision)
Every operation rereads and validates the index, referenced headers, text ranges
and active-message count under the byte-0 .JHR record lock. A session read returns
SessionMessage with a raw-byte SHA-256 revision. Changes to other messages do not
invalidate it. The revision includes identity, physical index/header/text locations
and SHA-256 over raw header, subfield and text bytes. An omitted patch field
retains its raw metadata; explicit None clears optional metadata. Names, subject, text and attributes reject None.
control_lines replaces general controls in both header subfields and inline
text. MSGID, address controls and routing retain their separate fields when
omitted; conflicting explicit controls are rejected. external_id and routing
patches remove obsolete inline copies as well as replacing their header metadata.
Body-only changes retain existing inline controls and routing. Unknown subfields
and nonzero HiID values survive unless their supported field is explicitly changed.
Content updates append a header, subfields and text, then redirect the index. Header-only changes retain the text bytes and record position. Unknown subfields, reserved words, timestamps and reply links remain intact. Delete marks the header, sets the recipient index CRC to FFFFFFFF, and decrements the active count. Message numbers and lastread data remain unchanged.
CP850 is the default. Encoding is strict; conflicting charset declarations, truncation, unsupported compression flags and arbitrary reply lists are refused. No MSGID, routing or duplicate detection is generated. Full-file snapshots support in-place rollback during ordinary I/O failures; rollback failure poisons the session. This provides no process-kill or power-loss transaction guarantee.
GoldED coexistence is disabled on every platform (concurrent=True is refused).
macOS/Linux use POSIX record locking. Core provides Windows offline record locks
and I/O, but this checkout has only been tested on macOS. Windows and Linux
execution and GoldED interoperability remain unverified. Keep GoldED closed and
avoid direct file access while these sessions operate. GoldED builds and
integration tests are deferred.
Original-source evidence
The reference checkout is golded-open-source, commit
600266252b73174ff5116cee697ef9a97aeb1859. It contains
goldlib/gmb3/gmojamm.h (JamHdrInfo,
JamHdr, JamIndex, subfield IDs), gmojamm2.cpp (open_area initialization,
scan indexing), and gmojamm4.cpp (lock, unlock, save_message).
lock takes byte 0 length 1 in .JHR; save_message maintains recipient/MSGID/
REPLY CRCs, active count and modification counter. Its CRC seed and missing final
complement are confirmed by goldlib/gall/gcrcs32.cpp::strCrc32.
These source checks establish layout and write semantics. They do not establish
that a running GoldED reader refreshes safely after external changes.
Metadata
Release files for golded-ftn-jam 1.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| golded_ftn_jam-1.2.1.tar.gz | 31.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| golded_ftn_jam-1.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.1 kB
Release files / golded_ftn_jam-1.2.1.tar.gz
| Download URL | golded_ftn_jam-1.2.1.tar.gz |
|---|---|
| Size | 31.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2226a8079360e08d97ee235851b7851e8736ab6423ed1a2746e61c424f6bffd3
|
|
BLAKE2b-256 checksum How to use checksums |
6c1bea1075147e0c3545af763e5c6a40f59dd571873cac765344534dd5649c5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency logRelease files / golded_ftn_jam-1.2.1-py3-none-any.whl
| Download URL | golded_ftn_jam-1.2.1-py3-none-any.whl |
|---|---|
| Size | 18.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
595d310bd878105c4d932961f0d59e6a13ece2845fa6b08dcb85a1d3bb88fc31
|
|
BLAKE2b-256 checksum How to use checksums |
45676e82995eb4672038c816c1f1d70ce8161f93d18248aa14e741225152084e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency log