Ralonv0.1.6
[01]

The policy file

One file at the project root. Every command finds it by walking up from the working directory, the same way git finds .git.

agent.lock
version: 1  # required, must be 1

protect:  # paths relative to this file
 - .env            # a file
 - config          # a directory, and everything under it
 - config/**       # the same thing, spelled out
 - src/*.ts        # * stops at /
 - "**/secrets.json" # ** does not
  • agent.lock always protects itself, whether or not you list it.
  • .., absolute paths, ~ and ! negation are rejected at parse time rather than quietly reinterpreted. A policy that does not mean what it says is worse than no policy.
  • Unknown keys and unknown versions are errors, so a file written for a later Ralon fails loudly instead of being half-applied.
[02]

Patterns

PatternMatchesDoes not match
.envthat exact filesrc/.env
configthe directory and everything beneath it, at any depthconfiguration
config/**identical to the above — the directory itself is included, so it cannot be renamed out from under the policy
src/*.tssrc/auth.tssrc/deep/auth.ts* stops at a separator
**/secrets.jsonthe file at any depth, including the rootsecrets.json.bak

Matching is case-sensitive on Linux and case-insensitive on Windows and macOS. On a case-insensitive filesystem, a deny list that matched fewer paths than the filesystem does would be wrong in the dangerous direction.

[03]

Commands

CommandDoes
ralon installRegisters the per-user background supervisor — a Task Scheduler logon task on Windows, a launchd LaunchAgent on macOS. Additive: re-running it never drops a scope. Fails on Linux, with the reason. --scope names a directory, --no-hooks skips configuring agents, --dry-run registers nothing.
ralon scope add|list|removeThe directories a policy is honoured in. Where Ralon is installed does not decide this — a home directory on C: says nothing about a repository on D:. Scopes are kept disjoint and canonical; add and remove reconcile before returning.
ralon pause / resumeReleases one project so its own policy can be edited, because agent.lock protects itself. Expires after fifteen minutes unless --indefinitely is given.
ralon uninstallDeregisters the supervisor and releases every project it held. --keep-enforcement leaves the enforcement in place with nothing watching it.
ralon statusThe policy, the protected paths on disk, the backends this kernel offers, and three separate answers: is the supervisor registered, is it running, and is this project protected.
ralon check [paths...]Reports whether the given paths are protected, exiting 1 if any are. With no arguments, lists everything the policy protects right now.
ralon run -- <cmd>Restricts the current process and replaces it with the command. --dry-run prints the plan without enforcing it; --backend pins the choice; --quiet drops the summary line.
ralon guardOne project's enforcement, held with no command to supervise — what the supervisor starts, by hand. --detach backgrounds it, --stop releases it.
ralon initWrites a starter agent.lock and configures the agents. Refuses to overwrite a policy without --force. Not needed under a supervisor: writing the file by hand is the whole step.
ralon hook installWrites the refusal into nine agents' own configuration, so a blocked write reads as "protected by Ralon" rather than EBUSY. Done automatically for each project the supervisor enforces.
ralon daemonThe supervisor itself, started by the operating system rather than by people. --once does a single pass and prints what changed.

Every command takes --dir to point at a project other than the working directory. Supervisor state — the scopes, the recorded workspaces and the log — lives in %LOCALAPPDATA%\Ralon or ~/Library/Application Support/Ralon, relocatable with RALON_HOME.

[04]

Exit codes

These are the interface. A wrapper that swallows them reports every policy as satisfied.

CodeMeaning
0Fine.
1A path is protected (check), or the plan cannot be enforced here (--dry-run).
2Error: no policy, a bad policy, no usable backend, or the command failed to start.

Otherwise run exits with your command's own status, because it is your command by then.

[05]

Backends

run picks the strongest backend the kernel offers. ralon status shows what is available and --backend pins it.

mount — the default

Read-only bind mounts inside a user and mount namespace, locked by entering a second namespace so they cannot be undone. Every parent directory of a protected path becomes a mount point too, so no directory on the way to it can be renamed or removed. Precise: nothing outside the protected paths behaves differently. Needs unprivileged user namespaces, which some hardened distributions and container runtimes disable.

landlock — the fallback

The kernel LSM, Linux 5.13+. Needs no namespaces, so it works exactly where the mount backend cannot. Landlock rules are additive — a rule may grant more access than its parents, never less — so "everything except this file" has to be expressed by granting every sibling along the way instead.

The consequence is visible and worth knowing: directories leading to a protected path stop accepting new entries. With src/index.tsx protected, everything in src/ and the project root stays writable, but new files cannot be created directly in either — inside tests/ or any other subtree they are fine. ralon run --dry-run --backend landlock lists exactly which directories are affected.