diff --git a/README.md b/README.md index bc2ce46..beeb6f6 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,20 @@ It registers one provider, `signature`, for the `signed-webdav` scheme. ## Connecting -| Field | Meaning | -| --- | --- | -| Server address | The folder's address, for example `https://dav.example.com/files/` | -| Username | Optional account name for HTTP basic authentication | -| Password | Optional password, stored as a secret | -| Signer name | The ENS name that signs, for example `laptop.alice.eth` | -| Signing key | The private key of the address the name resolves to, stored as a secret | -| Sepolia RPC address | The endpoint used to check signers against ENS, by default `https://ethereum-sepolia-rpc.publicnode.com` | +![The connection form](https://git.capsulizers.com/commons/memona-plugin-signature/raw/branch/main/images/connection-form.png) + +| Field | Key | Meaning | +| --- | --- | --- | +| Server address | `url` | The folder's address, for example `https://dav.example.com/files/` | +| Username | `username` | Optional account name for HTTP basic authentication | +| Password | `password` | Optional password, stored as a secret | +| Signer name | `ensName` | The ENS name that signs, for example `laptop.alice.eth` | +| Signing key | `privateKey` | The private key of the address that owns the name, stored as a secret | +| Sepolia RPC address | `rpcUrl` | The endpoint used to check signers against ENS, by default `https://ethereum-sepolia-rpc.publicnode.com` | +| Trusted parent name | `trustedParent` | Optional. Only signers that are this name or one of its subnames count, for example `team.eth` | + +The connection's allowed hosts must include the RPC address's host as well as +the server's, or every check reads as Sepolia unreachable. Listing, reading, writing, creating directories, renaming and deleting behave as in the WebDAV plugin: reads and writes stream in pieces of at most 1 MiB, @@ -36,15 +42,44 @@ and the body's SHA-256, made with the configured key; other frontmatter and the body stay byte for byte. A key that is not 32 bytes of hex refuses the connection, and a `.md` file that is not UTF-8 refuses the save. +![A note saved in Memona, signed and verified](https://git.capsulizers.com/commons/memona-plugin-signature/raw/branch/main/images/after-save.png) + ## Verifying -Reading a `.md` file of up to 40 KiB checks its signature against ENSv2 on -Sepolia through the RPC address and adds an `ens-verification` line to the -frontmatter Memona receives, which Memona shows as a badge; a larger one up to -1 MiB is marked as not checked. Both limits keep hashing and key recovery -inside the instruction budget Memona gives each call. The line never -reaches the server: a save drops it before signing, and one stored on the -server is replaced. Verdicts are reused for 30 seconds per signed body. +Reading a `.md` file checks its signature against ENSv2 on Sepolia and adds an +`ens-verification` line to the frontmatter Memona receives, which Memona shows +as a badge. The owner of the signer name comes from the Universal Resolver's +`findOwner`, so a name its parent revoked stops counting at once. + +| Badge | Meaning | +| --- | --- | +| `Verified: bob.team.eth` | The name's current owner signed this exact body | +| `Tampered: signed as bob.team.eth` | The body changed after signing, or another key signed it | +| `Unauthorized: bob.team.eth is not registered` | The signature holds, but no one owns the name now: it was revoked or never granted | +| `Unauthorized: bob.team.eth is not a member of team.eth` | The signer is outside the trusted parent name | +| `Unsigned` | The file has no `signer` and `signature` | +| `Not checked: Sepolia unreachable` | The RPC address did not answer, so nothing was decided | +| `Not checked: file too large` | The file is over 40 KiB, too large to hash within one call | + +| Verified | Tampered | +| --- | --- | +| ![Verified](https://git.capsulizers.com/commons/memona-plugin-signature/raw/branch/main/images/verified.png) | ![Tampered](https://git.capsulizers.com/commons/memona-plugin-signature/raw/branch/main/images/tampered.png) | + +| Unauthorized | Unsigned | +| --- | --- | +| ![Unauthorized](https://git.capsulizers.com/commons/memona-plugin-signature/raw/branch/main/images/unauthorized.png) | ![Unsigned](https://git.capsulizers.com/commons/memona-plugin-signature/raw/branch/main/images/unsigned.png) | + +The line never reaches the server: a save drops it before signing, and one +stored on the server is replaced, so a file cannot claim its own verdict. +Verdicts are reused for 30 seconds per signed body, a failed lookup included, +so an editor rereading a file does not flood the RPC. Stat, list and saves +keep reporting the stored size and the server's etag, so `If-Match` works as +before. + +Memona gives each call into a plugin a fixed instruction budget, which is where +the size limits come from: checking hashes the body and recovers a key, so it +covers files up to 40 KiB; a file up to 1 MiB is marked not checked, and a +larger one streams through without a badge. ## Building diff --git a/images/after-save.png b/images/after-save.png new file mode 100644 index 0000000..138f6dc Binary files /dev/null and b/images/after-save.png differ diff --git a/images/connection-form.png b/images/connection-form.png new file mode 100644 index 0000000..7a10014 Binary files /dev/null and b/images/connection-form.png differ diff --git a/images/tampered.png b/images/tampered.png new file mode 100644 index 0000000..529636a Binary files /dev/null and b/images/tampered.png differ diff --git a/images/unauthorized.png b/images/unauthorized.png new file mode 100644 index 0000000..4d69547 Binary files /dev/null and b/images/unauthorized.png differ diff --git a/images/unsigned.png b/images/unsigned.png new file mode 100644 index 0000000..4f7ec68 Binary files /dev/null and b/images/unsigned.png differ diff --git a/images/verified.png b/images/verified.png new file mode 100644 index 0000000..ba17c84 Binary files /dev/null and b/images/verified.png differ diff --git a/manifest.json b/manifest.json index c730656..ac62456 100644 --- a/manifest.json +++ b/manifest.json @@ -44,7 +44,7 @@ { "key": "privateKey", "label": "Signing key", - "description": "The private key of the address this name resolves to", + "description": "The private key of the address that owns this name", "kind": "password", "required": true },