In the architecture of secure systems, there is a dangerous myth that software should attempt to survive all errors. Austral rejects this "survival-at-all-costs" heuristic. Instead, we adopt a philosophy of deliberate termination, prioritizing system integrity and capability-based security over the futile attempt to recover from a corrupted state.
- The Mers-el-Kébir Principle: A Strategic Necessity
To understand why Austral chooses to crash rather than continue, we look to a sobering historical precedent.
"On July 3, 1940, as part of Operation Catapult, the Royal Air Force bombed the ships of the French Navy—their own allies—stationed at Mers-el-Kébir. This was not an act of malice, but a strategic necessity to prevent the fleet from falling into the hands of the Third Reich and becoming an asset for the enemy."
Just as the British scuttled an allied fleet to prevent its subversion, an Austral program terminates immediately upon a contract violation to prevent its "unforgeable authority" from becoming a weapon for an adversary.
In Austral’s security model, a contract violation is not a "oopsie"; it is a sign that the program’s internal state is suspect. Because Austral is built on Capability-Based Security, every active handle or pointer represents a specific piece of authority. If the program enters an invalid state, we must assume that authority is compromised. By "scuttling the ship," the runtime ensures that a corrupted capability cannot be leveraged into a supply chain attack or a remote exploit.
To survive the enemy, we must first name the enemy.
- Categorizing the "Enemy": The Taxonomy of Errors
Not every problem in a program is a fatal wound. To maintain "fits-in-head simplicity," we distinguish between different categories of failure and dictate a rigid defensive response for each.
Error Category Nature of Threat Austral's Defensive Response
Physical Failure Hardware destruction or power loss. No direct action possible. While the process dies, software can be designed to persist data (e.g., Databases) to survive the next boot.
Abstract Machine Corruption Stack overflow or internal runtime failure. Terminate. The execution environment is compromised; recovery is counterproductive and dangerous.
Contract Violations Logic bugs: division by zero, array index out of bounds, or violated invariants. Scuttle (Terminate). The program is in an unpredictable state. Assume an adversary is present and neutralize the process.
Memory Allocation Failure malloc returns null or memory is fragmented. Recoverable Value. Reject the myth that OS overcommit makes checking pointless. We return an Option or Result because "Linux is not the only platform."
Failure Conditions Environmental issues: "File not found" or "Connection timed out." Recoverable Value. These are predictable outcomes of well-constructed programs. Handle via Option and Result types.
Contract Violations are the primary target of our termination directive. In Austral, a bug is not a failure—it is a breach of the fundamental laws of the program.
- Comparing Defensive Strategies: Why We Choose Termination
Modern languages generally handle contract violations in one of three ways. Austral chooses the most "brittle" to ensure system health.
- Terminate Program on Error (TPOE): Immediate exit. No cleanup. No destructors.
- Terminate Thread on Error (TTOE): Kills the specific task. Parent threads observe the crash but the program lives on.
- Raise Exception on Error (REOE): Traditional "try/catch" with stack unwinding and destructor calls.
Why TPOE is the Architect's Choice
- Security and the "Hidden Function Call" Risk: REOE relies on implicit calls to destructors during stack unwinding. If a program is already corrupted, executing these "invisible" calls can provide an attacker with a vector to hijack control flow. TPOE is a clean break.
- Corruption and Broken Invariants: Unwinding a stack does not repair a broken data structure. If an exception is caught, the remaining program is often left with "suspect" data that can trigger secondary, even more subtle vulnerabilities.
- The "Double Throw" Honest Reality: Consider the C function fclose. In systems using RAII/Exceptions, if fclose fails during a stack unwind (which was already triggered by an error), the system usually aborts anyway. Austral’s TPOE is simply more honest: it acknowledges that if the system is failing, the cleanup itself is likely to fail or be subverted.
- The Technical Conflict: Linear Types vs. Exceptions
Austral’s primary safety mechanism is its Linear Type System. Linearity ensures that resources—memory, file handles, or database connections—are used exactly once. This safety is mathematically incompatible with traditional exceptions.
The primary risk is the Resource Leak. If an exception is thrown after a resource is created but before it is consumed, that resource vanishes from the type system's tracking but remains "alive" in the system.
The "Threading" Conflict
In Austral, linear resources are "threaded" through function calls to maintain ownership:
-- Correct "Threading" of a Linear Resource
let f1: File := openFile("data.txt"); -- Resource 'f1' created
let f2: File := writeString(f1, "Hi"); -- 'f1' consumed, 'f2' created
closeFile(f2); -- 'f2' consumed. State is clean.
Now consider a hypothetical exception scenario:
let f1: File := openFile("data.txt");
throw InternalError; -- Exception jumps over the next lines!
let f2: File := writeString(f1, "Hi"); -- Never reached
closeFile(f2); -- Never reached. 'f1' is leaked.
While some languages use Affine Types (allowing values to be silently discarded by the compiler), Austral rejects this "weakening." Affine types "bend the rules to fit the programmer" by inserting implicit calls. Austral requires the programmer to bend to the rules. By rejecting exceptions and affine types, we ensure the system remains "crystalline"—every resource is accounted for, or the program does not compile.
- Operational Excellence: Errors vs. Failures
In Austral, we distinguish between a fatal Error (a bug) and a manageable Failure (an environmental event).
- Errors (Bugs): Accessing the 11th element of a 10-element array is an error. There is no "recovery" for being wrong. The program must terminate.
- Failures (Conditions): A wrong password or a network timeout is a failure. These are modeled as values. We use Option and Result types to force the developer to handle the "unhappy path" explicitly at compile time.
As Sussman and Abelson noted, "Pascal is for building pyramids—imposing, breathtaking, static structures built by armies pushing heavy blocks into place." Austral is a language for building pyramids. We prioritize a structure that is static, rigid, crystalline, and brittle. Minor changes should break the build, and errors should crash the process, because a "brittle" system is one that does not hide its flaws behind opaque heuristics.
- Summary: The Security Mandate
The "Scuttle the Ship" philosophy is a deliberate design choice to strip away the "opaque pile of heuristics" found in modern runtimes. In Austral, we sacrifice ergonomics for the sake of implementational simplicity and absolute correctness.
Key Takeaways for the Aspiring Austral Developer:
- Termination is a Security Feature: Crashing is the only way to guarantee that a corrupted capability is not exploited.
- Assume an Adversary: The moment a contract is violated, the state is suspect. Do not attempt to "recover" and risk providing an attacker with a vector.
- Linearity is Absolute: Linear types provide manual memory management safety that exceptions would only undermine.
- Failures are Values: Use Option and Result for expected issues. If you encounter a bug, let the program die.
By embracing this rigidity, you move toward a system that is simple enough to fit in a single person's head, and secure enough to run in a hostile world.
Top comments (0)