mmurl://

Multi-Resource Uniform Locator · v0.5.0 · experimental

One identifier that opens a whole working context.

A URL addresses one resource. An mURL addresses a set of them — a repository, its docs, its dashboard, a working directory, a terminal — under a single name that can be inspected before anything happens.

brew install thatwasyahya/murl/murl

format v0.2 · Rust reference + Python implementation · MIT OR Apache-2.0

Resolution of murl://acme.example/project-x One mURL fans out to six resources: a GitHub repository, documentation and a Grafana dashboard (SAFE), a working directory and a notes file (SENSITIVE), and a terminal (DANGEROUS). murl://acme.example/project-x parse → fetch manifest → validate → verify → policy source https https://github.com/acme/project-x SAFE docs https https://docs.acme.example/project-x SAFE monitoring https https://grafana.acme.example/d/px SAFE workspace dir ~/projects/project-x SENSITIVE notes file ~/projects/project-x/NOTES.md SENSITIVE term terminal ~/projects/project-x DANGEROUS

6 resources1 consent step0 shells

What an mURL is

A name for a set, with a document in between.

URL
identifier one resource
mURL
identifier manifest { source docs workspace term }

The indirection through a manifest is the whole point. Because the set is a document rather than a string, it has a canonical byte form — so it can be hashed, signed, versioned, cached, diffed, and above all shown to you before it acts. Publishing a namespace means serving static JSON under /.well-known/murl/: no registry, no account, no new infrastructure. An mURL is a name, never a container.

This is a proposal with a working implementation, not a standard. The murl scheme is unregistered, no third party has adopted it, and the format may still change before 1.0. Read it as a design worth arguing with — the specification page says what that means in practice.

What resolution looks like

The plan is printed before anything opens.

Real output from murl resolve against the example destination in the repository. Every leaf is an ordinary URL or path handed to the application you already use; mURL is a composition layer above locators, not a replacement for them.

murl — resolve
$ murl resolve murl://local/demo/project-x
Project X  (murl://local/demo/project-x)
  Everything that makes up the Project X development context.
  manifest: local store (~/.local/share/murl/names/demo/project-x.murl.json)  trust: LOCAL
  manifest: local store (~/.local/share/murl/names/demo/team.murl.json)  trust: LOCAL
  resources:
    ├─ source      SAFE       https      https://github.com/example/project-x
    ├─ docs        SAFE       https      https://docs.example.com/project-x
    ├─ issues      SAFE       https      https://jira.example.com/projects/PX
    ├─ monitoring  SAFE       https      https://grafana.example.com/d/project-x
    ├─ workspace   SENSITIVE  dir        ~/projects/project-x
         ⚠ consent: SENSITIVE resource
    ├─ term        DANGEROUS  terminal   ~/projects/project-x
         ⚠ consent: DANGEROUS resource
    ├─ wiki        SAFE       https      https://wiki.example.com/team
    └─ chat        SAFE       https      https://chat.example.com/#/room/team
$ 
  1. resolve

    murl resolve prints the fully resolved plan: every resource after nested destinations are spliced in (wiki and chat come from team), its tier, where each manifest came from and whether it is trusted. Nothing opens.

  2. inspect

    The plan is the thing you agree to. Consent to a name would be meaningless — a name can point anywhere — so consent is always to the set, tier by tier, with local paths spelled out.

  3. consent

    murl open shows the same plan and asks once. Each resource then goes to its handler as an argv array: browser, file manager, terminal app. No target string ever reaches a shell.

Three tiers, decided locally

Classified by what a resource is, never by what a manifest claims.

An identifier that can make an operating system open many things is an attack primitive by default. So the tier derives from kind plus target, the policy lives in local, user-owned configuration, and no manifest content can reach it.

SAFE
https

Rendered by a sandboxed browser. Worst case is roughly a tab.

Consent — one consolidated step for the whole set
Trust not required; an expired manifest still prompts, with a warning
SENSITIVE
file, dir

Non-executable local paths. Opening one exposes or touches local data.

Consent — always, with the path shown
A remote manifest pointing at local disk adds an extra consent reason, or is denied by policy
DANGEROUS
terminal, custom:* + executable-adjacent files

Opening it is, or is one dialog away from, code execution. A file that is really a .desktop, .lnk, .sh or .exe lands here too — classification looks at the target, not the label.

Consent + trust — the manifest is installed locally, or signed by a key you pinned
Untrusted: denied. Not a scarier prompt — a refusal

That last 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 removes the social-engineering path rather than warning about it. What remains for an attacker is getting you to run murl trust add or murl name add — deliberate, documented acts.

The full security model, and what it does not defend against

  • SAFE resourceprompt
  • SENSITIVE resourceprompt
  • DANGEROUS resource, manifest trustedprompt
  • DANGEROUS resource, manifest untrusteddeny — not promptable
  • Expired or not-yet-valid manifestSAFE prompts · rest denied
  • Scripted consent--yes covers SAFE only

Install

Two binaries; only murl is required.

Installing registers nothing and starts nothing. Making murl:// clickable is a separate, explicit step: murl os install.

bash
cargo install murl-cli

# optional: the resident consent surface
cargo install murl-daemon

The crate is murl-cli; the binary it installs is murl, in ~/.cargo/bin. Needs Rust 1.75 or newer.

bash
brew install thatwasyahya/murl/murl

macOS and Linux, from the thatwasyahya/murl tap.

powershell
scoop bucket add murl https://github.com/thatwasyahya/scoop-murl && scoop install murl

Windows, from the scoop-murl bucket.

powershell
winget install thatwasyahya.murl

pending review  Submitted to winget-pkgs with all checks green; this command does not work until a maintainer merges it. Use Scoop or cargo on Windows meanwhile.

bash
git clone https://github.com/thatwasyahya/mURL && cd mURL
git checkout v0.5.0
cargo install --path crates/murl-cli
cargo install --path crates/murl-daemon   # optional; the consent dialog on macOS

Four crates, pure Rust, unsafe_code forbidden workspace-wide. cargo test and bash examples/demo.sh are worth running once before trusting it with anything.

The first five minutes

murl resolve before murl open is the habit worth forming: it prints the fully resolved plan, every tier, and the trust status of the manifest, without dispatching anything.

bash
murl create --name "Project X"                # write a starter manifest
murl validate project-x.murl.json
murl name add project-x project-x.murl.json   # install as murl://local/project-x

murl resolve murl://local/project-x           # see the full plan — nothing opens
murl open murl://local/project-x              # consent, then dispatch
murl open 'murl://local/project-x#docs'       # just one part of it

murl keygen && murl sign project-x.murl.json  # sign it
murl os install                               # make murl:// clickable
222
tests, including the security suites
153
conformance vectors any implementation can run
2
implementations: Rust reference, Python
0
shell invocations, ever — every launch is argv