The Filesystem Problem
A filesystem is the bridge between raw storage and structured data. It answers the questions every program asks: Where is my file? How large is it? Which blocks on disk belong to it? Is this block free or occupied? What is the file's name, and how do I find it in a directory hierarchy?
Every modern filesystem — ext4, NTFS, APFS, ZFS — answers these questions assuming binary addressing. Block addresses are binary integers. Free-space maps are bitmaps (one bit per block: free or used). Inode numbers are binary. Directory entries are indexed by binary hash functions.
None of these assumptions hold when storage is ternary. If the underlying hardware uses balanced ternary addressing — as the PANINI processor and T3ISA define — then the filesystem must be redesigned from first principles. That is what TritFS does.
Trit-Based Addressing: 3n Instead of 2n
In binary, an n-bit address can reference 2n locations. In balanced ternary, an n-trit address references 3n locations — but with a twist. Balanced ternary addresses are signed: they range from −(3n−1)/2 to +(3n−1)/2, centred on zero.
| Address Width | Binary Range | Balanced Ternary Range | Ternary Locations |
|---|---|---|---|
| 8 | 0 to 255 | −3,280 to +3,280 | 6,561 |
| 16 | 0 to 65,535 | −21.5M to +21.5M | 43,046,721 |
| 27 | 0 to 134M | −3.8T to +3.8T | 7.6 × 1012 |
The 27-trit address width is not arbitrary — it matches the T3ISA register width (33 = 27 trits per register), so a single register holds a full address. The resulting address space of 327 = 7,625,597,484,987 locations is vastly larger than the equivalent binary width, which is one of the density advantages of ternary encoding.
Inode Design Considerations
In traditional Unix filesystems, an inode is a fixed-size data structure that describes a file: its size, permissions, timestamps, and the list of data blocks it occupies. The inode number is a binary integer used as an index into the inode table.
In TritFS, inode numbers are balanced ternary values. This has a subtle but important consequence: inode 0 is a valid, usable inode, not a sentinel value. In many binary filesystems, inode 0 is reserved or unused because zero is treated as "null." In balanced ternary, zero is just the middle value — it has no special null semantics. If you need a null sentinel, you can use a value outside the valid range, but you do not lose inode 0.
The inode itself stores block pointers as balanced ternary addresses. Direct block pointers, single-indirect, and double-indirect pointers follow the same hierarchy as traditional Unix inodes — but each pointer is a trit-addressed value pointing into the ternary block space.
Space Allocation: Beyond Bitmaps
Binary filesystems track free space using bitmaps: one bit per block, where 0 means free and 1 means allocated (or vice versa). This is efficient because each bit is the minimum unit of information needed to track a two-state property.
In a ternary filesystem, each block position in the allocation map holds a trit — three states instead of two. The obvious question is: what does the third state represent?
Two states (free and allocated) are sufficient for basic block tracking. But filesystems have long struggled with intermediate states — blocks that are "in transition" during a write operation, blocks reserved for a file that is being extended, or blocks that are being freed but cannot be reused until a journal commit completes. In binary bitmaps, these intermediate states require separate data structures or journal entries to track.
A ternary allocation map offers a natural third state, eliminating the need for some of these auxiliary structures. The specific semantics of the three states in TritFS are defined in Patent P8, but the principle is straightforward: three states in the hardware should be reflected in three states in the allocation map.
Journalling with Three States
Journalling filesystems protect against data corruption by recording intended changes before applying them. The journal entry lifecycle in a binary filesystem is typically two-state: a transaction is either "committed" or "not committed." Some filesystems add soft states (e.g., ext4's "running," "committing," "finished"), but these are encoded as multi-bit fields, not single-bit flags.
With a trit-state journal, each transaction entry inherently supports three states in its most basic flag field. Consider the general problem: a filesystem operation (say, writing a block) needs to transition through stages — intent recorded, data written, metadata updated. In a binary journal, tracking these stages requires multiple flag bits or sequential log entries. A ternary journal flag can encode three stages in a single trit.
The specific three-state journalling protocol used in TritFS is the subject of Patent P8, Claims 7-8. What I can say here is that the protocol reduces journal overhead compared to binary equivalents by encoding more state information per journal entry field.
The ASCII Encoding Challenge
Filesystems store filenames, and filenames are character strings. ASCII defines 128 characters (7-bit encoding). How do you encode ASCII in balanced ternary?
The mathematics is direct:
How many trits to encode 128 characters?
log₃(128) = ln(128) / ln(3) ≈ 4.42
→ 4 trits encode 3⁴ = 81 values (not enough)
→ 5 trits encode 3⁵ = 243 values (sufficient!)
5 trits per character, with 243 − 128 = 115 spare codes
available for ternary-native extensions.
Five trits per ASCII character is slightly more efficient than 7 bits when measured in information density: 5 trits carry 5 × log2(3) = 7.92 bits of information, encoding 128 characters with room to spare. The 115 unused codes can accommodate extended character sets, control sequences, or ternary-specific symbols without needing a separate encoding like UTF-8.
TritFS filenames use this 5-trit-per-character encoding. Maximum filename length follows from the directory entry size, which is defined in trit-aligned units.
Directory Structure: Enter the Trit-Trie
Directory lookup — finding a file by name — is one of the most performance-critical filesystem operations. Binary filesystems use various strategies: linear search (simple but slow), hash tables (fast but unordered), or B-trees (ordered and fast, but complex).
For a ternary filesystem with trit-encoded filenames, there is a natural data structure: the trie (prefix tree), where each node has exactly three children — one per trit value (−1, 0, +1). This "trit-trie" provides O(k) lookup time where k is the key length, with no hash collisions and natural lexicographic ordering.
The trit-trie is sufficiently important that it has its own patent (P9) and its own blog post (coming soon). TritFS Claim 9 explicitly references the trit-trie as the directory index structure, creating a continuation dependency between P8 and P9.
What TritFS Defines
Putting it all together, TritFS specifies:
- Trit-addressed inodes — file metadata in ternary-native format
- Ternary block allocation map — three states per block position
- Trit-trie directory index — O(k) filename lookup (Patent P9)
- Trit-state journalling — three-phase transaction protocol
- 5-trit ASCII encoding — 243-code character space
- Trit-aligned block sizes — block sizes are powers of 3 (243, 729, 2187 trits)
The complete specification is Patent P8, filed under IPC G06F 16/10 (file systems).
TritFS is not a theoretical exercise — it is the filesystem that THATTE-OS uses
for persistent storage, and its structures are compiled into the kernel's
mem.t3b module.
- Ternary addressing gives 3n locations per n-trit address — exponentially denser than binary
- Ternary allocation maps offer a natural third state for block status tracking
- ASCII encodes in 5 trits per character (243 codes), with 115 spare codes for extensions
- Trit-tries (Patent P9) provide O(k) directory lookup with no hash collisions
- TritFS is the first filesystem designed from scratch for balanced ternary storage hardware