Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ Read-only — no auth, no mutations, no server roundtrip beyond the GETs.

**2. Nostr verification-method generator** — reads your Nostr pubkey from a [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md) signer (e.g. [xlogin](https://xlogin.solid.social/)), encodes it per [did:nostr](https://nostrcg.github.io/did-nostr/)'s Multikey recipe, and emits a copyable JSON snippet to add to your profile. No keys leave your browser.

**3. Strict [LWS10-CID](https://www.w3.org/TR/2026/WD-lws10-authn-ssi-cid-20260423/) auth client** — sign in to your pod via Solid-OIDC (using the [`solid-oidc`](https://www.npmjs.com/package/solid-oidc) package), paste a secp256k1 private key as 64 hex chars (the raw 32-byte key behind your Nostr `nsec1…` bech32 — same key, different signature scheme), and the doctor adds a `JsonWebKey` VM to your profile (read-modify-write via authenticated GET + PUT) and signs an LWS10-CID JWT with `alg: ES256K` to authenticate end-to-end. Pairs with the [JSS server-side verifier](https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/pull/398). Privkey is held in memory for the tab only.

## Roadmap (rough)

- ~~**B.0**~~ — Read-only LWS-CID profile validator ✅
- ~~**B.2**~~ — Read pubkey from NIP-07 signer; emit Multikey verificationMethod snippet ✅
- ~~**B.3**~~ — Strict LWS10-CID auth: Solid-OIDC sign-in, ES256K `JsonWebKey` VM written into profile (GET → merge → PUT with `If-Match`), sign real JWTs to authenticate ✅
- **B.1** — Bidirectional `alsoKnownAs` ↔ DID-doc check (resolve `did:nostr:…` and verify the DID points back at this WebID)
- **B.3** — In-app PATCH of the snippet via Solid-OIDC sign-in (closes the loop end-to-end)
- **B.4** — did:key + WebAuthn passkey verification methods
- **B.5** — More diagnostics: ACL inheritance, type-index integrity, OIDC discovery, ActivityPub actor doc, …

Expand Down
130 changes: 130 additions & 0 deletions doctor.css
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,133 @@ a { color: var(--accent); }
.add-key pre {
margin: 0;
}

/* --- B.3: strict LWS-CID auth section ----------------------------- */

.lws-auth {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
margin-top: 20px;
}
.lws-auth h2 {
margin: 0 0 6px;
font-size: 18px;
}
.lws-auth h3 {
margin: 18px 0 8px;
font-size: 14px;
}
.lws-auth > p {
margin: 0 0 18px;
color: var(--muted);
font-size: 13px;
}
.lws-auth p.hint {
font-size: 12px;
color: var(--muted);
margin: 0 0 8px;
}
.lws-auth code {
background: #eef2f7;
padding: 1px 5px;
border-radius: 4px;
font-size: 12px;
}

.oidc-status {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--muted);
margin-bottom: 12px;
}
.oidc-status .dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--skip);
flex-shrink: 0;
}
.oidc-status.signed-in .dot { background: var(--pass); }
.oidc-status.error .dot { background: var(--fail); }

.lws-auth button {
background: var(--accent);
color: #fff;
border: 0;
padding: 9px 16px;
border-radius: 8px;
font: inherit;
font-weight: 600;
cursor: pointer;
}
.lws-auth button:hover:not(:disabled) { background: #1d4ed8; }
.lws-auth button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.lws-auth button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.lws-auth #oidc-signout {
background: #475569;
margin-left: 6px;
}
.lws-auth #oidc-signout:hover:not(:disabled) { background: #334155; }

.lws-auth label {
display: block;
font-size: 12px;
font-weight: 600;
margin-bottom: 4px;
}
.lws-auth input[type="password"] {
width: 100%;
padding: 9px 12px;
border: 1px solid var(--border);
border-radius: 8px;
font: inherit;
font-size: 13px;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
margin-bottom: 12px;
background: #fff;
}
.lws-auth input[type="password"]:focus {
outline: none;
border-color: var(--accent);
}

.patch-result, .test-result {
margin-top: 10px;
font-size: 12px;
padding: 8px 10px;
border-radius: 6px;
white-space: pre-wrap;
word-break: break-word;
}
.patch-result:empty, .test-result:empty {
display: none;
}
.patch-result.ok, .test-result.ok {
background: #ecfdf5;
color: #065f46;
border: 1px solid #a7f3d0;
}
.patch-result.error, .test-result.error {
background: #fef2f2;
color: #991b1b;
border: 1px solid #fecaca;
}
.patch-result.info, .test-result.info {
background: #eff6ff;
color: #1e3a8a;
border: 1px solid #bfdbfe;
}
pre.test-result {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
}
Loading