oaknut-afs
A Python library for reading, writing, and creating Acorn Level 3 File Server (AFS) disc partitions — the private on-disc filesystem the Level 3 File Server served to BBC Micro, Master, and Archimedes clients over Econet.
AFS is a hierarchical, multi-user filesystem: it has real directories, a
per-file owner and public access model, and a $.Passwords file of user
accounts. It is identified by the AFS0 magic in its info sectors.
Part of a dual-partition disc. An AFS filesystem does not occupy a disc alone — it lives in the tail cylinders of an old-map ADFS disc. The real
WFSINITutility (and oaknut'sinitialise()) shrinks the ADFS partition and carves the AFS region out of the space freed. oaknut-afs reads and writes that region; oaknut-adfs handles the host.
Part of the oaknut monorepo.
What it does
- Read and write an AFS partition: browse the directory tree, read and write files, create and remove directories, and edit metadata (owner and public access, the native two-byte datestamp).
- Partition and initialise (
initialise()): theWFSINITanalogue — repartition an old-map ADFS disc and lay down an empty AFS filesystem in the tail, with its map, root, and$.Passwordsfile. - Manage users (
UserSpec,PasswordsFile): create accounts with passwords and disc-space allocations; the built-inSyst,Boot, andWelcomeaccounts are handled. - Import a host tree (
import_host_tree()): populate a new filesystem from a directory of files with.infsidecars. - Merge (
merge()) two AFS partitions, and emplace shipped libraries (SHIPPED_LIBRARIES) such as the standardLibrary.
Passwords are stored as the file server stored them: up to six cleartext ASCII
bytes in $.Passwords, with no encryption — oaknut reproduces the format
faithfully rather than obscuring it.
Installation
uv add oaknut-afs # or: pip install oaknut-afs
oaknut-afs works with any PEP 517 build
front-end and package manager; the examples use
uv.
Usage
Creating and populating a filesystem
from oaknut.afs import AFS, UserSpec
with AFS.create_file(
"server.dat",
capacity="10MB",
disc_name="Server",
users=[UserSpec(name="RJS", quota="2MB")],
) as afs:
# Creating the RJS account also creates its home directory, $.RJS.
(afs.root / "RJS" / "ReadMe").write_text("Welcome to the file server.\n")
Reading
from oaknut.afs import AFS
with AFS.from_file("server.dat") as afs:
for entry in afs.root.iterdir():
print(entry.name)
text = (afs.root / "RJS" / "ReadMe").read_text()
For dual ADFS + AFS discs, the
disc
CLI reaches the AFS partition through the ADFS host with an afs: prefix
(disc ls 'server.dat:afs:$'), routed by content identification.
Development
The package is developed in the oaknut workspace. From the repository root:
uv sync # install all workspace members editable
uv run pytest packages/oaknut-afs/tests # this package's tests
uv run ruff check # lint
Architecture
oaknut-afs depends on oaknut-adfs (to place and read the partition within its
ADFS host), oaknut-discimage, and oaknut-file, and contributes AFS to the
oaknut.filesystem extension axis so that AFS partitions are identified,
listed, and read through the disc CLI alongside the disc-based filing
systems. Sector-level access comes from
oaknut-discimage;
metadata and the acorn codec from
oaknut-file.
Further reading
- AFS0 on-disc format — Rick Murray's notes on the Level 3 File Server's private format.
- Acorn Econet — the network the file server served over.
License
MIT — see LICENSE.
Metadata
Release files for oaknut-afs 13.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| oaknut_afs-13.1.0.tar.gz | 208.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| oaknut_afs-13.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 368.4 kB
Release files / oaknut_afs-13.1.0.tar.gz
| Download URL | oaknut_afs-13.1.0.tar.gz |
|---|---|
| Size | 208.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e28d1a0dd254751c40d19829f30612fd2d4e575f9b5cb705edf9e4cf0b867c82
|
|
BLAKE2b-256 checksum How to use checksums |
b1c6e51e88e4634671f99237e08183f987a02589069e7722da06c997d1c0c305
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / oaknut_afs-13.1.0-py3-none-any.whl
| Download URL | oaknut_afs-13.1.0-py3-none-any.whl |
|---|---|
| Size | 159.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90b97cc025ba2ebeaa85c22b0b21518ff10e90aa99058b00287381072e2f9538
|
|
BLAKE2b-256 checksum How to use checksums |
c0957267fea42c324a24830cc3dcdb92aeb9c032b794777e5e2c3e56ae300e1d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|