mmurl://

Security model

Manifests propose. Local policy disposes.

An identifier that can make an operating system open many things is an attack primitive by default. This model exists to make it a useful primitive instead.

Three tiers, decided locally safe sensitive dangerous

This page is a summary. The security model document states the model in full, and the threat model enumerates the attacks it answers, one by one, with an honest status on each — mitigated, partial, or planned.

Five principles

  1. Manifests propose; local policy disposes. A manifest describes what a destination is made of. It carries no permissions, cannot request permissions, and cannot configure handlers. Everything privileged — policy, trust pins, handler registrations, limits — lives in local, user-owned configuration that no manifest content can reach.

  2. Consent before dispatch, on the real plan. You are shown the fully resolved plan — every resource after splicing, its tier, the manifest's origin and trust status — before anything opens. Consent to a name is meaningless; consent is to the set.

  3. No shell, anywhere. Every launch is an argv array passed to the process API, with target substitution confined to a single argv element. There is no code path in which a target string reaches sh -c, cmd /c, or any other shell.

  4. Fail closed. A non-interactive invocation with no explicit allow flags dispatches nothing that needed consent. A hostile document that triggers the scheme handler in some headless context gets a refusal, not a best effort.

  5. Hostile input stays behind small, fuzzed parsers. The mURL parser, the manifest validator, and the canonicalizer are total functions with hard input caps, fuzzed continuously.

Three tiers

Classification derives from what a resource is — kind plus target — never from what a manifest says about itself.

SAFE

https

Rendered by a sandboxed browser; worst case is roughly a tab.

SENSITIVE

file, dir

Non-executable local paths. Exposes or touches local data.

DANGEROUS

terminal, custom:*, executable-adjacent files

.exe, .bat, .ps1, .sh, .desktop, .lnk, .jar, .hta and friends. Opening it is, or is one dialog from, code execution.

The executable-extension escalation closes a classic laundering trick — "it is just a file", where the file is a .desktop entry and the desktop's opener would execute it. Classification looks at the target, not the label.

What each tier requires

These are defaults. All of them are overridable in local config.json — and by nothing else.

SituationDefault outcome
safe resourceprompt (one consolidated consent step)
sensitive resourceprompt
dangerous resource, manifest trustedprompt
dangerous resource, manifest untrusteddeny — not promptable
Remote manifest referencing local filesystemextra consent reason, or deny by policy
Expired or not-yet-valid manifestsafe prompts with a warning; everything else denied

The untrusted-DANGEROUS rule is the keystone. Users can be talked into one click, and one click is exactly what a hostile mURL gets. Making terminals from unknown senders un-clickable rather than merely scary removes the social-engineering path entirely; an attacker's remaining routes are getting the user to run murl trust add or murl name add, both deliberate, documented acts.

Consent flags for scripts are tier-scoped — --yes covers SAFE only, with separate --allow-sensitive and --allow-dangerous — so automation has to state exactly how much risk it is accepting.

FlagConsents to
--yessafe resources only
--allow-sensitivesensitive resources
--allow-dangerousdangerous resources — still only when the manifest is trusted

Trust

"Trusted" has one meaning here: the manifest is installed locally, or it carries a valid ed25519 signature from a key you pinned for that authority. There is no certificate authority and no web of trust — see the trust model for why not.

  • Signatures cover a canonical byte form (MCF-1), so a manifest that is reformatted but not altered still verifies.
  • Identity binding: a manifest declaring an id is refused when served under a different name, so a valid signature cannot be replayed to relabel content.
  • Integrity pins: a nested mURL can be pinned by sha256-… over the raw bytes of the manifest it resolves to; a mismatch is a hard stop.
  • Pinned versions are immutable by contract, and @latest is explicitly a mutable alias with a stated rollback window.

Hardening around it

Resolution

Userinfo is forbidden by the grammar, so murl://github.com@evil… does not parse. Identifiers are ASCII-only and IDN authorities must be punycoded. TLS is required (loopback excepted), redirects are not followed, the size cap is enforced while reading, and DNS results that resolve only to private, link-local, loopback or CGNAT ranges are refused.

Limits as controls

Depth ≤ 3, ≤ 8 manifests, ≤ 64 resources, ≤ 256 KiB per manifest, cycle detection on the resolution path, duplicate-target suppression. Every breach is a hard error, and none of them can be loosened by manifest content.

Dispatch

argv-only process creation; a target containing ; rm -rf ~ stays one inert argument. Local targets must be absolute and dot-segment free, and are existence-checked before launch. Launching is sequential and staggered, which bounds the blast radius of even an approved 64-resource plan.

Custom kinds

custom:* dispatches only through handlers the user registered out of band. An unregistered custom kind fails; it never guesses a handler.

What it does not defend against

Stated plainly, because a security model is defined by its edges.

  • A malicious trusted manifest. If you pin a key or install a manifest locally, its DANGEROUS resources are one consent away. Trust is the boundary; choose pins accordingly.
  • What handlers do after launch. Once the browser has the URL or the terminal is open, mURL's control ends. It does not sandbox handlers.
  • Rollback of @latest. An attacker who captured an old, validly signed manifest can replay it until it expires. expires bounds the window; monotonic version enforcement — a transparency log — is out of scope before 1.0.
  • Desktop-environment consent gaps. On Linux, scheme activation opens a terminal to ask for consent; a terminal-less or compromised session degrades to fail-closed rather than to a GUI prompt. Providing a native consent surface is the resident daemon's reason to exist.
  • Local attackers. Config, trust store and name store are plain files under the user's account, protected by OS file permissions only. An attacker who can write to them has already won the account.
  • Phishing by name. The grammar removes the mechanical tricks, but a plausible-looking authority is still a plausible-looking authority. This is a partially-mitigated threat, and the threat model says so.