diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 16d93e4..8241931 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -108,6 +108,11 @@ jobs: - name: Staging config tests run: npm --prefix examples/js-sdk run test:staging-config + - name: JS example behavior tests + run: | + npm --prefix examples/js-sdk run test:reader-load-state + npm --prefix examples/js-sdk run test:pubky + - name: Paykit setup-status contract run: npm --prefix examples/js-sdk run check:paykit-setup-contract diff --git a/Cargo.lock b/Cargo.lock index a73a99f..ee9b013 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,6 +273,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64ct" version = "1.8.3" @@ -1403,7 +1409,7 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", @@ -1713,7 +1719,7 @@ dependencies = [ [[package]] name = "locks-core" -version = "0.1.0-rc2" +version = "0.1.0-rc4" dependencies = [ "base32", "blake3", @@ -1729,7 +1735,7 @@ dependencies = [ [[package]] name = "locks-e2e" -version = "0.1.0-rc2" +version = "0.1.0-rc4" dependencies = [ "async-trait", "axum", @@ -1748,7 +1754,7 @@ dependencies = [ [[package]] name = "locks-sdk" -version = "0.1.0-rc2" +version = "0.1.0-rc4" dependencies = [ "locks-core", "paykit-lib", @@ -1765,7 +1771,7 @@ dependencies = [ [[package]] name = "locks-sdk-wasm" -version = "0.1.0-rc2" +version = "0.1.0-rc4" dependencies = [ "futures-lite", "js-sys", @@ -1787,12 +1793,12 @@ dependencies = [ [[package]] name = "locks-server" -version = "0.1.0-rc2" +version = "0.1.0-rc4" dependencies = [ "anyhow", "async-trait", "axum", - "base64", + "base64 0.22.1", "clap", "locks-core", "locks-service", @@ -1823,10 +1829,10 @@ dependencies = [ [[package]] name = "locks-service" -version = "0.1.0-rc2" +version = "0.1.0-rc4" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "blake3", "chacha20poly1305", "locks-core", @@ -1838,6 +1844,7 @@ dependencies = [ "thiserror", "time", "tokio", + "tracing", "url", "uuid", ] @@ -2136,7 +2143,7 @@ version = "0.1.0-rc48" source = "git+https://github.com/pubky/paykit-rs.git?tag=v0.1.0-rc48#9b56a0eacd6874137370fa79ec0f40b809140809" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "chacha20poly1305", "chrono", "pubky", @@ -2370,11 +2377,10 @@ checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "pubky" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a7b191157d57d5095f1577c8064aeda85f96606957fcf4c0a6f4b675adb96d" +source = "git+https://github.com/pubky/pubky-homeserver.git?rev=99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03#99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03" dependencies = [ "async-trait", - "base64", + "base64 0.23.1", "cookie", "eventsource-stream", "flume 0.11.1", @@ -2382,6 +2388,7 @@ dependencies = [ "futures-util", "httpdate", "log", + "lru 0.18.3", "percent-encoding", "pkarr", "pubky-common", @@ -2401,11 +2408,10 @@ dependencies = [ [[package]] name = "pubky-common" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee26f4cf7d9d11a300180b351f1ea0efebf17ec66f0e615a8867f8aba47dc5f" +source = "git+https://github.com/pubky/pubky-homeserver.git?rev=99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03#99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03" dependencies = [ "argon2", - "base64", + "base64 0.23.1", "blake3", "crypto_secretbox", "ed25519-dalek", @@ -2671,7 +2677,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "cookie", "cookie_store", @@ -3272,7 +3278,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "crc", "crossbeam-queue", @@ -3349,7 +3355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "bytes", @@ -3393,7 +3399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "crc", @@ -3538,7 +3544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", diff --git a/Cargo.toml b/Cargo.toml index 68f61e2..95f8252 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ resolver = "3" [workspace.package] edition = "2024" -version = "0.1.0-rc2" +version = "0.1.0-rc4" license = "MIT" repository = "https://github.com/pubky/locks" rust-version = "1.91.1" @@ -29,8 +29,8 @@ locks-core = { path = "locks-core" } locks-service = { path = "locks-service" } mime = "0.3" paykit-lib = { git = "https://github.com/pubky/paykit-rs.git", tag = "v0.1.0-rc48" } -pubky = { version = "0.11.0", features = ["json"] } -pubky-common = "0.11.0" +pubky = { version = "0.11.0", git = "https://github.com/pubky/pubky-homeserver.git", rev = "99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03", features = ["json"] } +pubky-common = { version = "0.11.0", git = "https://github.com/pubky/pubky-homeserver.git", rev = "99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03" } pubky-noise = "=0.1.0-rc7" qrcode = { version = "0.14", default-features = false, features = ["svg"] } pkarr = "8.0.0" @@ -60,3 +60,7 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } url = "2" uuid = { version = "1", features = ["v4", "js"] } + +[patch.crates-io] +pubky = { git = "https://github.com/pubky/pubky-homeserver.git", rev = "99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03" } +pubky-common = { git = "https://github.com/pubky/pubky-homeserver.git", rev = "99a2fb12f0ba4d3f7d9ff4b7b7b0740dabb18d03" } diff --git a/README.md b/README.md index a54d413..3b9b996 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ docker compose --file compose.paykit-local-demo.yaml up -d --build 3. In production, use the production Bitkit QR/deep-link path presented by Paykit. When using the local CLI authentication fallback, run `npm --prefix examples/js-sdk ...` commands from the repository host. Do not wrap `authenticate` or `authenticate-paykit` in `docker compose exec`; those wrappers load private role state on the host and bridge only the bounded native-helper request into the demo container. The helper is supplied only by the Paykit local-demo image/runtime stage, not the normal production package/runtime. Follow the manual bearer-URL log retrieval and retention guidance in the example README. -Its external build contexts use anonymously reachable public repositories selected by immutable version tags; no sibling Paykit or Pubky checkout is required. Pubky Testnet is built from the `pubky/pubky-homeserver` `v0.11.0` tag, Paykit libraries use the `v0.1.0-rc48` tag, Paykit Server uses `v0.1.0-rc2`, and Paykit's compatible Locks context uses `v0.1.0-rc1`. The local Paykit Server worktree override remains available through `PAYKIT_SERVER_CONTEXT`. The full Paykit demo adds Paykit Server at . The reader remains at in every local flow. Payment remains a manual operator action. +Paykit Server uses merged commit `26bda476b9fa1d29feb87cbb24a90042d00f42c4` and is built against the current Locks worktree so both services use the same protocol paths. The local Paykit Server worktree override remains available by exporting an absolute `PAYKIT_SERVER_CONTEXT` path before running Compose. Other external build contexts remain anonymously reachable and version-tagged: Pubky Testnet uses `pubky/pubky-homeserver` `v0.11.0`, and Paykit libraries use `v0.1.0-rc48`. The full Paykit demo adds Paykit Server at . The reader remains at in every local flow. Payment remains a manual operator action. For the helper-free loopback browser demo against deployed staging Locks and Paykit services: @@ -166,11 +166,18 @@ For the implemented authorization boundary, see Lock Server as a Pubky application can optionally provide functionality for content creator to: - Create guarded content on the creator's homeserver -- Create public lock policies under `/pub/locks.app/*` +- Create public lock policies under `/pub/app.locks/*` - Store verified proof bundles under a guarded Locks path Alternatively, content creators can create guarded content and public lock policies using other available methods. +The `app.locks` namespace is a breaking replacement for the retired `locks.app` +namespace. Existing public locks are not migrated automatically and must be +republished. Existing creator grants for the retired paths must be authorized +again for `/pub/app.locks/` and `/priv/app.locks/`. Pending verification records +that reference retired lock resources should be completed or discarded before +upgrading; no database migration rewrites those resource identifiers. + Lock Server as a Pubky application provides functionality for content viewer to: - Read public lock conditions @@ -192,19 +199,19 @@ Minimum for verification plus proxy access: A future narrower target could look like: ```text -[/priv/locks.app/content/:r, /priv/locks.app/proofs/:rw] +[/priv/app.locks/content/:r, /priv/app.locks/proofs/:rw] ``` If the Lock Server also creates guarded content and public lock policies for the creator, it additionally needs write access to the guarded content namespace and: ```text -[/pub/locks.app/:rw] +[/pub/app.locks/:rw] ``` Until narrower guarded capabilities are implemented, the interim grant may need to be broader, for example: ```text -[/priv/:rw, /pub/locks.app/:rw] +[/priv/:rw, /pub/app.locks/:rw] ``` This trade-off should be explicit: a broad `/priv/:rw` grant means the creator is trusting the Lock Server with all guarded payloads, not only Locks-specific payloads. The desired end state is a Locks-specific private namespace. @@ -243,7 +250,7 @@ For credible exit, the default Lock Server pointer should be centralized rather Recommended default location: ```text -/pub/locks.app/config.json +/pub/app.locks/config.json ``` Current v0 Lock Service Pointer shape: @@ -286,13 +293,13 @@ This needs more careful consideration and should not be the default in this draf 1. Content creator authorizes the Lock Server or creator app with the required capabilities. 2. Content creator uploads guarded content to their homeserver, preferably under a Locks-specific private namespace once supported. 3. Content creator computes a hash of the guarded content. -4. Content creator defines lock conditions and uploads the public lock policy to `/pub/locks.app/.json`. +4. Content creator defines lock conditions and uploads the public lock policy to `/pub/app.locks/.json`. 5. Content creator creates a preview post anywhere, such as a pubky.app post, pointing to the public lock policy. Example preview text: ```text -Check out my locked content at pubky/pub/locks.app/.json +Check out my locked content at pubky/pub/app.locks/.json ``` Guarded content write should not trigger a public `/events` entry. Public lock policy write should trigger `/events`. @@ -311,14 +318,14 @@ sequenceDiagram H-->>C: 200 JWT Note over C,H: Step 2: Store guarded payload - C->>H: PUT /priv/locks.app/content/ [header: JWT] + C->>H: PUT /priv/app.locks/content/ [header: JWT] H-->>C: 200 OK Note over C,C: Step 3: Hash guarded payload C->>C: blake3(content) Note over C,H: Step 4: Create lock policy - C->>H: PUT /pub/locks.app/.json [header: JWT] + C->>H: PUT /pub/app.locks/.json [header: JWT] H-->>C: 200 OK H-->>H: Emit /events @@ -333,8 +340,8 @@ sequenceDiagram #### Flow 3.2 1. Viewer discovers lock either through a preview post or via `/events` endpoint. Event discovery may be missing human context. -2. Viewer reads public unlock conditions from `pubky/pub/locks.app/.json`. -3. Viewer resolves the Lock Server using `/pub/locks.app/config.json`, unless the lock policy contains a service override. +2. Viewer reads public unlock conditions from `pubky/pub/app.locks/.json`. +3. Viewer resolves the Lock Server using `/pub/app.locks/config.json`, unless the lock policy contains a service override. 4. Viewer solves the lock-specific challenge or gathers required proof material. 5. Viewer submits a proof bundle to the Lock Server. The current HTTP server applies a configurable process-local fixed-window admission limit before creating verification work. 6. Lock Server verifies the submitted proof bundle. @@ -358,11 +365,11 @@ sequenceDiagram H-->>V: preview + lock link Note over V,H: Step 2: Get unlock conditions - V->>H: GET /pub/locks.app/.json + V->>H: GET /pub/app.locks/.json H-->>V: LockPolicy JSON Note over V,H: Step 3: Resolve Lock Server - V->>H: GET /pub/locks.app/config.json + V->>H: GET /pub/app.locks/config.json H-->>V: default Lock Server pointer Note over V,V: Step 4: Lock-specific process @@ -385,7 +392,7 @@ sequenceDiagram end alt Verification succeeds - L->>H: PUT /priv/locks.app/proofs/.json [header: JWT] + L->>H: PUT /priv/app.locks/proofs/.json [header: JWT] H-->>L: 200 OK L-->>V: { "creator": "...", "bundle_id": "...", "status": "completed" } else Verification fails @@ -394,7 +401,7 @@ sequenceDiagram Note over V,L: Step 10: Access content V->>L: GET opaque bearer credential returned by `POST /access-credentials` - L->>H: GET /priv/locks.app/content/ [header: JWT] + L->>H: GET /priv/app.locks/content/ [header: JWT] H-->>L: 200 OK L-->>V: 200 OK ``` @@ -403,7 +410,7 @@ sequenceDiagram 1. Creator revokes the old Lock Server session/grant. 2. Creator grants the new Lock Server access to guarded content and stored verified proof bundles. -3. Creator updates `/pub/locks.app/config.json` with the new Lock Server pointer. +3. Creator updates `/pub/app.locks/config.json` with the new Lock Server pointer. 4. If a lock policy used a per-lock service override, that lock policy also needs to be updated. 5. Viewer presents their stored `bundle_id` to the new Lock Server. 6. New Lock Server reads the verified proof bundle from the creator's guarded Locks proof path. @@ -420,7 +427,7 @@ Because verified proof bundles are stored on the creator's homeserver, migration Public path: ```text -pubky/pub/locks.app/.json +pubky/pub/app.locks/.json ``` Example: @@ -430,13 +437,13 @@ Example: "version": 1, "creator": "pubky", "primary_resource": { - "path": "/priv/locks.app/content/post.json", + "path": "/priv/app.locks/content/post.json", "hash": "", "content_type": "application/json", "size": 1234 }, "secondary_resources": { - "/priv/locks.app/content/attachments/image.png": { + "/priv/app.locks/content/attachments/image.png": { "hash": "", "content_type": "image/png", "size": 4567 @@ -490,7 +497,7 @@ Example: { "version": 1, "bundle_id": "", - "pubky_lock_resource": "pubky/pub/locks.app/.json", + "pubky_lock_resource": "pubky/pub/app.locks/.json", "reader_public_key": "pubky", "proofs": [ { @@ -515,7 +522,7 @@ A verified proof bundle is stored only after successful verification. It functio Guarded path example: ```text -pubky/priv/locks.app/proofs/.json +pubky/priv/app.locks/proofs/.json ``` Example: @@ -526,10 +533,10 @@ Example: "bundle_id": "", "status": "verified", "creator": "pubky", - "pubky_lock_resource": "pubky/pub/locks.app/.json", + "pubky_lock_resource": "pubky/pub/app.locks/.json", "lock_hash": "", "resource_set": { - "primary_path": "/priv/locks.app/content/post.json", + "primary_path": "/priv/app.locks/content/post.json", "resource_hashes": [ "", "" diff --git a/compose.paykit-local-demo.yaml b/compose.paykit-local-demo.yaml index cff8bd5..a4a39c5 100644 --- a/compose.paykit-local-demo.yaml +++ b/compose.paykit-local-demo.yaml @@ -281,12 +281,12 @@ services: paykit-server: image: pubky-locks-paykit-server:local build: - context: "${PAYKIT_SERVER_CONTEXT:-https://github.com/pubky/paykit-server.git#v0.1.0-rc2}" + context: "${PAYKIT_SERVER_CONTEXT:-https://github.com/pubky/paykit-server.git#26bda476b9fa1d29feb87cbb24a90042d00f42c4}" dockerfile: Dockerfile.local additional_contexts: paykit-lib: "https://github.com/pubky/paykit-rs.git#v0.1.0-rc48:paykit-lib" paykit-sdk: "https://github.com/pubky/paykit-rs.git#v0.1.0-rc48:paykit-sdk" - locks: "https://github.com/pubky/locks.git#v0.1.0-rc1" + locks: . depends_on: paykit-postgres: diff --git a/docker/bitcoin-bootstrap.sh b/docker/bitcoin-bootstrap.sh index 22533fb..79f9218 100755 --- a/docker/bitcoin-bootstrap.sh +++ b/docker/bitcoin-bootstrap.sh @@ -39,4 +39,9 @@ if (( height < 101 )); then bitcoin_cli -rpcwallet=miner generatetoaddress "$((101 - height))" "$address" >/dev/null fi +if bitcoin_cli getblockchaininfo | grep -q '"initialblockdownload": true'; then + address="$(bitcoin_cli -rpcwallet=miner getnewaddress)" + bitcoin_cli -rpcwallet=miner generatetoaddress 1 "$address" >/dev/null +fi + printf '%s\n' 'bitcoin bootstrap ready' diff --git a/docs/ADRs/0001-locks-application-layer-trusted-proxy.md b/docs/ADRs/0001-locks-application-layer-trusted-proxy.md index 1645237..b8405f0 100644 --- a/docs/ADRs/0001-locks-application-layer-trusted-proxy.md +++ b/docs/ADRs/0001-locks-application-layer-trusted-proxy.md @@ -28,5 +28,5 @@ Negative: ## Open Follow-ups -- Confirm the final Pubky Homeserver path capability model for `/priv/locks.app/`. -- Confirm whether app sessions can write verified proof bundles under `/priv/locks.app/proofs/`. +- Confirm the final Pubky Homeserver path capability model for `/priv/app.locks/`. +- Confirm whether app sessions can write verified proof bundles under `/priv/app.locks/proofs/`. diff --git a/docs/ADRs/0003-hash-derived-lock-id.md b/docs/ADRs/0003-hash-derived-lock-id.md index c784cf4..1c318a6 100644 --- a/docs/ADRs/0003-hash-derived-lock-id.md +++ b/docs/ADRs/0003-hash-derived-lock-id.md @@ -26,7 +26,7 @@ Canonicalization and encoding: - Lock ID parsing uses `base32::decode` with the Crockford alphabet, including that crate's built-in lowercase and ambiguous-character normalization. - Lock ID parsing rejects hyphens/readability separators. - Lock ID uses no Crockford checksum. -- Public lock path: `/pub/locks.app/.json`. +- Public lock path: `/pub/app.locks/.json`. Lock ID and lock hash are derived values, not serialized fields inside the content lock payload, to avoid circular hashing. @@ -51,4 +51,4 @@ Negative: ## Open Follow-ups -- Add test vectors for canonical JSON, BLAKE3, Crockford-base32 Lock ID encoding, and final `/pub/locks.app/.json` path. +- Add test vectors for canonical JSON, BLAKE3, Crockford-base32 Lock ID encoding, and final `/pub/app.locks/.json` path. diff --git a/docs/ADRs/0004-lock-server-discovery-and-session-handling.md b/docs/ADRs/0004-lock-server-discovery-and-session-handling.md index 60ad485..0662ce9 100644 --- a/docs/ADRs/0004-lock-server-discovery-and-session-handling.md +++ b/docs/ADRs/0004-lock-server-discovery-and-session-handling.md @@ -14,7 +14,7 @@ The design considered whether Lock Server discovery should use creator-owned pub The default Lock Server pointer stays in creator-owned public config: ```text -/pub/locks.app/config.json +/pub/app.locks/config.json ``` It can be overridden by a Lock Server location specified in a concrete content lock using this nested field shape: @@ -27,7 +27,7 @@ It can be overridden by a Lock Server location specified in a concrete content l } ``` -Absence of `lock_server.override` means viewers use the default Lock Server pointer from `/pub/locks.app/config.json`. +Absence of `lock_server.override` means viewers use the default Lock Server pointer from `/pub/app.locks/config.json`. Because all serialized content lock payload fields participate in the lock hash, changing `lock_server.override` creates a new Lock ID. diff --git a/docs/ADRs/0009-v0-verification-result-shape.md b/docs/ADRs/0009-v0-verification-result-shape.md index 79ecb34..d351e48 100644 --- a/docs/ADRs/0009-v0-verification-result-shape.md +++ b/docs/ADRs/0009-v0-verification-result-shape.md @@ -28,7 +28,7 @@ Each criterion verification result includes: v0 criterion results do not include arbitrary lock-type-specific metadata. The verified proof bundle instead references the exact content lock file using `pubky_lock_resource`. -`VerifiedProofBundle` uses `pubky_lock_resource`, not `content_lock`, because the field value is the protocol-facing addressed Pubky resource: `pubky/pub/locks.app/.json`. To honor an entitlement, the Lock Server derives creator, content lock path, and Lock ID from this resource, reads the content lock file, and verifies that it hashes to the embedded Lock ID. +`VerifiedProofBundle` uses `pubky_lock_resource`, not `content_lock`, because the field value is the protocol-facing addressed Pubky resource: `pubky/pub/app.locks/.json`. To honor an entitlement, the Lock Server derives creator, content lock path, and Lock ID from this resource, reads the content lock file, and verifies that it hashes to the embedded Lock ID. ## Consequences diff --git a/docs/ADRs/0011-identifier-and-canonicalization-contracts.md b/docs/ADRs/0011-identifier-and-canonicalization-contracts.md index 6e97a4a..b21cdac 100644 --- a/docs/ADRs/0011-identifier-and-canonicalization-contracts.md +++ b/docs/ADRs/0011-identifier-and-canonicalization-contracts.md @@ -72,7 +72,7 @@ Rules: Rules: -- Must be exactly `/pub/locks.app/.json`. +- Must be exactly `/pub/app.locks/.json`. - Not a full Pubky URL. - Not a homeserver URL. - Not any other `/pub/...` path. @@ -85,7 +85,7 @@ Rules: Rules: -- Must be exactly `pubky/pub/locks.app/.json`, matching the preferred `PubkyResource` identifier form from the `pubky` crate. +- Must be exactly `pubky/pub/app.locks/.json`, matching the preferred `PubkyResource` identifier form from the `pubky` crate. - Must not use the alternate `pubky://...` form in Locks protocol payloads. - Must not be an HTTP(S) homeserver transport URL. - Embedded creator must parse as `CreatorPubky`. @@ -107,6 +107,6 @@ Protocol timestamps use `time::OffsetDateTime` and serialize as RFC3339 JSON str - `base32` crate behavior is part of the implementation contract and should be covered by tests for lowercase, ambiguous Crockford characters, and hyphen rejection. - `BundleId` and `TaskId` are visibly distinct wherever both appear internally; public HTTP API payloads use the `{ creator, bundle_id }` verification handle and do not expose `TaskId`. - Content lock paths have one canonical creator-relative shape and do not accept arbitrary Pubky URLs. -- Pubky lock resources have one canonical protocol-facing shape: `pubky/pub/locks.app/.json`. +- Pubky lock resources have one canonical protocol-facing shape: `pubky/pub/app.locks/.json`. - `locks-core` relies on `pubky::PublicKey` for Pubky identity parsing instead of reimplementing syntax checks. - Canonical JSON and timestamp behavior are explicit protocol contracts, not incidental implementation choices. diff --git a/docs/ADRs/0013-lock-server-api-boundaries.md b/docs/ADRs/0013-lock-server-api-boundaries.md index a4a4551..fc3eea6 100644 --- a/docs/ADRs/0013-lock-server-api-boundaries.md +++ b/docs/ADRs/0013-lock-server-api-boundaries.md @@ -120,7 +120,7 @@ POST /creator/lock-service-config -> SetLockServicePointerUseCase -> dev/test-gated local creator publishing route -> body includes creator and default Lock Server identity - -> stores/replaces local creator config whose canonical future Pubky path is /pub/locks.app/config.json + -> stores/replaces local creator config whose canonical future Pubky path is /pub/app.locks/config.json GET /healthz -> runtime liveness only @@ -143,7 +143,7 @@ The proxy-read response returns raw bytes, not JSON/base64. Successful proxy-rea Creator publishing routes are local dev/test product surface, not production Pubky publishing. `POST /creator/priv-resources`, `POST /creator/content-locks`, and `POST /creator/lock-service-config` are unauthenticated while local because real creator authorization depends on Pubky capability/session work. They must be mounted only when an explicit creator-publishing config flag is enabled in dev/test runtime, and production runtime must reject configs that expose them. Do not add fake shared-secret/admin-token auth. -`POST /creator/priv-resources` overwrites the current local guarded resource by creator/path. `POST /creator/content-locks` verifies that the referenced guarded resource currently exists and matches path, hash, content type, and size before creating a content lock. `POST /creator/lock-service-config` stores the creator's default Lock Service Pointer for `/pub/locks.app/config.json`, but content lock creation does not require it. These semantics exercise local creator-to-viewer flow without adding fake Pubky writes. +`POST /creator/priv-resources` overwrites the current local guarded resource by creator/path. `POST /creator/content-locks` verifies that the referenced guarded resource currently exists and matches path, hash, content type, and size before creating a content lock. `POST /creator/lock-service-config` stores the creator's default Lock Service Pointer for `/pub/app.locks/config.json`, but content lock creation does not require it. These semantics exercise local creator-to-viewer flow without adding fake Pubky writes. Proxy-read accepts credentials only through `Authorization: Bearer `. The `Bearer` scheme is case-insensitive, but parsing is otherwise strict: exactly one `Authorization` header, exactly one non-empty token after the scheme, no query-string credential, and no body credential. Missing or malformed bearer credentials map to `401 invalid_access_credential`. diff --git a/docs/ADRs/0016-creator-publishing-local-http-api.md b/docs/ADRs/0016-creator-publishing-local-http-api.md index 1cc8226..41f696f 100644 --- a/docs/ADRs/0016-creator-publishing-local-http-api.md +++ b/docs/ADRs/0016-creator-publishing-local-http-api.md @@ -22,7 +22,7 @@ POST /creator/content-locks POST /creator/lock-service-config ``` -`POST /creator/priv-resources` and `POST /creator/content-locks` form the two-step guarded-resource/content-lock publishing flow. `POST /creator/lock-service-config` stores the creator default Lock Service Pointer whose canonical future Pubky path is `/pub/locks.app/config.json`. +`POST /creator/priv-resources` and `POST /creator/content-locks` form the two-step guarded-resource/content-lock publishing flow. `POST /creator/lock-service-config` stores the creator default Lock Service Pointer whose canonical future Pubky path is `/pub/app.locks/config.json`. These routes are mounted only in dev/test runtime when an explicit creator-publishing flag is enabled. Production mode must refuse to expose them. The routes are unauthenticated in the local skeleton; do not add fake shared-secret auth or admin tokens. Real creator authorization remains a future Pubky capability/session concern. @@ -32,7 +32,7 @@ These routes are mounted only in dev/test runtime when an explicit creator-publi ```json { - "path": "/priv/locks.app/content/example.txt", + "path": "/priv/app.locks/content/example.txt", "hash": "", "content_type": "text/plain", "size": 5 @@ -47,7 +47,7 @@ These routes are mounted only in dev/test runtime when an explicit creator-publi `POST /creator/content-locks` creates a content lock for an already-registered guarded resource. The local use case verifies the referenced guarded resource currently exists for the creator and that path, hash, content type, and size match before storing the content lock. Content lock identity remains derived from canonical JSON: identical content lock creation is idempotent, while changed content creates a different lock ID and content lock path. -`/pub/locks.app/config.json` is the creator-owned default Locks config path, represented locally by a Locks-native `LockServicePointer` spec object. The local `POST /creator/lock-service-config` route stores the creator default Lock Server pointer. Content lock creation does not require this config to exist: content locks may use `lock_server.override`; when no override is present, viewer discovery depends on the creator's Lock Service Pointer. +`/pub/app.locks/config.json` is the creator-owned default Locks config path, represented locally by a Locks-native `LockServicePointer` spec object. The local `POST /creator/lock-service-config` route stores the creator default Lock Server pointer. Content lock creation does not require this config to exist: content locks may use `lock_server.override`; when no override is present, viewer discovery depends on the creator's Lock Service Pointer. Proxy-read returns raw guarded resource bytes and uses the stored guarded resource `content_type` for the HTTP `Content-Type` header. Successful proxy-read remains a byte response, not JSON/base64. @@ -65,7 +65,7 @@ Proxy-read returns raw guarded resource bytes and uses the stored guarded resour ## Deferred - Real creator authentication/authorization through Pubky capability grants. -- Pubky-backed writes under `/priv/locks.app/content/` and `/pub/locks.app/`. +- Pubky-backed writes under `/priv/app.locks/content/` and `/pub/app.locks/`. - Multipart or streaming uploads. - Range requests and content disposition. - Separate published `pubky-lock-specs` crate or npm/WASM package. diff --git a/docs/ADRs/0017-creator-granted-auth-boundary.md b/docs/ADRs/0017-creator-granted-auth-boundary.md index c097219..1910e75 100644 --- a/docs/ADRs/0017-creator-granted-auth-boundary.md +++ b/docs/ADRs/0017-creator-granted-auth-boundary.md @@ -19,13 +19,13 @@ Production Locks uses one creator-granted Locks app session per creator per Lock The required creator-granted scopes are: ```text -/pub/locks.app/:rw -/priv/locks.app/:rw +/pub/app.locks/:rw +/priv/app.locks/:rw ``` -`/pub/locks.app/:rw` covers public Locks resources such as the Lock Service Pointer and content lock files. `/priv/locks.app/:rw` covers guarded Locks resources such as guarded content, verified proof bundles / entitlement records, and proxy reads after viewer authorization succeeds. Locks stores guarded content bytes under `/priv/locks.app/content/` and verified proof bundles under `/priv/locks.app/proofs/.json`. The `/priv/locks.app/:rw` scope is sufficient for read/write/delete on all Locks private children; any Pubky SDK transport quirks stay inside infrastructure adapters rather than domain/use-case logic. +`/pub/app.locks/:rw` covers public Locks resources such as the Lock Service Pointer and content lock files. `/priv/app.locks/:rw` covers guarded Locks resources such as guarded content, verified proof bundles / entitlement records, and proxy reads after viewer authorization succeeds. Locks stores guarded content bytes under `/priv/app.locks/content/` and verified proof bundles under `/priv/app.locks/proofs/.json`. The `/priv/app.locks/:rw` scope is sufficient for read/write/delete on all Locks private children; any Pubky SDK transport quirks stay inside infrastructure adapters rather than domain/use-case logic. -Current Pubky Homeserver code supports the capability syntax and `/pub/locks.app/:rw` through existing `/pub/` homeserver write authorization. The confirmed homeserver path for Locks private data is `/priv/locks.app/`, so creator-granted Locks authority must include `/priv/locks.app/:rw` for private resources. Locks depends on Pubky homeserver private storage rather than implementing a parallel guarded storage/auth path. Current Pubky Homeserver emits events for public file writes/deletes and supports path-prefix event filtering. Private writes under `/priv/locks.app/...` emit no public events, and private paths are non-public; Locks discovery must use public `/pub/locks.app/...` resources rather than private write events. +Current Pubky Homeserver code supports the capability syntax and `/pub/app.locks/:rw` through existing `/pub/` homeserver write authorization. The confirmed homeserver path for Locks private data is `/priv/app.locks/`, so creator-granted Locks authority must include `/priv/app.locks/:rw` for private resources. Locks depends on Pubky homeserver private storage rather than implementing a parallel guarded storage/auth path. Current Pubky Homeserver emits events for public file writes/deletes and supports path-prefix event filtering. Private writes under `/priv/app.locks/...` emit no public events, and private paths are non-public; Locks discovery must use public `/pub/app.locks/...` resources rather than private write events. The first implemented creator authorization path is the existing Pubky QR/deeplink auth flow using the legacy/cookie session variant. After that legacy path is working, Locks should migrate creator authorization to the SDK grant flow (`PubkyGrantAuthFlow` / `GrantCredential`) as the durable production auth primitive. A Locks-specific UX/API wrapper is acceptable, but the Pubky protocol does not need a new auth primitive for the first implementation. Manual operator provisioning and direct client submission of raw session material are not production acquisition paths. @@ -78,7 +78,7 @@ Positive: Negative: - The Lock Server stores high-value creator-granted session material. -- `/priv/locks.app/:rw` gives Locks broad read/write authority within the Locks private namespace. +- `/priv/app.locks/:rw` gives Locks broad read/write authority within the Locks private namespace. - Proxy-read integrity verification adds read/hash cost to the first production implementation. - Creator status UX requires real Pubky/Ring token validation before it can be safely exposed. diff --git a/docs/API.md b/docs/API.md index c44e3f8..47af09e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -246,7 +246,7 @@ Authorized response: "creator": "pubkycreator123", "authorized": true, "auth_kind": "legacy_cookie", - "granted_scopes": ["/pub/locks.app/:rw", "/priv/locks.app/:rw"], + "granted_scopes": ["/pub/app.locks/:rw", "/priv/app.locks/:rw"], "session_expires_at": null } ``` @@ -303,7 +303,7 @@ Creator publishing routes always use Pubky homeserver-backed repositories. Calle Response-shape fixture: `locks-server/tests/fixtures/creator_publishing/register_guarded_resource_response_shape.json` -Registers or replaces the current guarded resource bytes for the authenticated creator and decoded relative content path. The server reconstructs the canonical private guarded resource path as `/priv/locks.app/content/`. With Pubky-backed repositories, it writes bytes to the creator homeserver under that path. +Registers or replaces the current guarded resource bytes for the authenticated creator and decoded relative content path. The server reconstructs the canonical private guarded resource path as `/priv/app.locks/content/`. With Pubky-backed repositories, it writes bytes to the creator homeserver under that path. #### Request @@ -320,7 +320,7 @@ The request body is raw resource bytes, not JSON. `Content-Type` is required and Path rules: - MUST be non-empty after percent-decoding. -- MUST be relative to `/priv/locks.app/content/`; callers supply only the relative path, not the full private path. +- MUST be relative to `/priv/app.locks/content/`; callers supply only the relative path, not the full private path. - MAY contain nested slash-separated segments. - MUST NOT start with `/`. - MUST NOT contain `..` traversal segments, including encoded traversal. @@ -335,7 +335,7 @@ Upload size is limited by `[content_locks].max_resource_bytes` and defaults to 1 { "creator": "pubkycreator123", "guarded_resource": { - "path": "/priv/locks.app/content/example.txt", + "path": "/priv/app.locks/content/example.txt", "hash": "", "content_type": "text/plain", "size": 13 @@ -378,7 +378,7 @@ Creates or replaces a content lock from a resource set. A content lock may conta - `primary_resource`: optional full [`GuardedResource`](#success-response) descriptor. - `secondary_resources`: optional map keyed by full canonical private path. Values contain `hash`, `content_type`, and `size` only. -At least one resource is required. If a primary resource is present, its path must not also appear in `secondary_resources`. `secondary_resources` keys are full canonical private paths such as `/priv/locks.app/content/attachments/a.txt`. +At least one resource is required. If a primary resource is present, its path must not also appear in `secondary_resources`. `secondary_resources` keys are full canonical private paths such as `/priv/app.locks/content/attachments/a.txt`. With Pubky-backed repositories, this writes the public content lock JSON to the creator homeserver under its derived `content_lock_path`. Test-support composition may use in-memory repositories behind the same authenticated route contract. @@ -401,13 +401,13 @@ Every referenced guarded resource must currently exist for the same creator/path ```json { "primary_resource": { - "path": "/priv/locks.app/content/post.json", + "path": "/priv/app.locks/content/post.json", "hash": "", "content_type": "application/json", "size": 123 }, "secondary_resources": { - "/priv/locks.app/content/attachments/a.txt": { + "/priv/app.locks/content/attachments/a.txt": { "hash": "", "content_type": "text/plain", "size": 13 @@ -438,18 +438,18 @@ Every referenced guarded resource must currently exist for the same creator/path ```json { "lock_id": "", - "content_lock_path": "/pub/locks.app/.json", + "content_lock_path": "/pub/app.locks/.json", "content_lock": { "version": 1, "creator": "pubkycreator123", "primary_resource": { - "path": "/priv/locks.app/content/post.json", + "path": "/priv/app.locks/content/post.json", "hash": "", "content_type": "application/json", "size": 123 }, "secondary_resources": { - "/priv/locks.app/content/attachments/a.txt": { + "/priv/app.locks/content/attachments/a.txt": { "hash": "", "content_type": "text/plain", "size": 13 @@ -496,9 +496,9 @@ Fixtures: - Request: `locks-server/tests/fixtures/creator_publishing/set_lock_service_config_request.json` - Response shape: `locks-server/tests/fixtures/creator_publishing/set_lock_service_config_response_shape.json` -Stores or replaces the creator's default Lock Service Pointer for the canonical Pubky path `/pub/locks.app/config.json`. +Stores or replaces the creator's default Lock Service Pointer for the canonical Pubky path `/pub/app.locks/config.json`. -With Pubky-backed repositories, this writes the pointer JSON to the creator homeserver at `/pub/locks.app/config.json`. Test-support composition may use in-memory repositories behind the same authenticated route contract. +With Pubky-backed repositories, this writes the pointer JSON to the creator homeserver at `/pub/app.locks/config.json`. Test-support composition may use in-memory repositories behind the same authenticated route contract. Content lock creation does not require a Lock Service Pointer. A content lock may carry `lock_server.override`; future viewer discovery can use the pointer when no override exists. @@ -515,7 +515,7 @@ Content lock creation does not require a Lock Service Pointer. A content lock ma ```json { "creator": "pubkycreator123", - "path": "/pub/locks.app/config.json", + "path": "/pub/app.locks/config.json", "lock_service_pointer": { "version": 1, "default_lock_server": "pubkyserver123", @@ -548,7 +548,7 @@ Request envelope: "submitted_proof_bundle": { "version": 1, "bundle_id": "", - "pubky_lock_resource": "pubky/pub/locks.app/.json", + "pubky_lock_resource": "pubky/pub/app.locks/.json", "reader_public_key": "pubky", "proofs": [ { @@ -570,7 +570,7 @@ Submission processing applies rate limiting, validates proof shape, loads the cu ```json { "bundle_id": "", - "lock_resource": "pubky/pub/locks.app/.json", + "lock_resource": "pubky/pub/app.locks/.json", "reader": "pubky" } ``` @@ -615,7 +615,7 @@ Response includes the raw credential exactly once. Polling routes never return c ### `GET /priv-resources/content/` -Proxy-reads one guarded resource from the content lock authorized by a bearer credential. The `` segment is the same relative path used for upload; the server reconstructs `/priv/locks.app/content/` and verifies that path is in the credential's content lock resource set before reading bytes. +Proxy-reads one guarded resource from the content lock authorized by a bearer credential. The `` segment is the same relative path used for upload; the server reconstructs `/priv/app.locks/content/` and verifies that path is in the credential's content lock resource set before reading bytes. Credentials are accepted only through: diff --git a/docs/DOMAIN_MODEL.md b/docs/DOMAIN_MODEL.md index 00629a7..2e906ea 100644 --- a/docs/DOMAIN_MODEL.md +++ b/docs/DOMAIN_MODEL.md @@ -52,7 +52,7 @@ Postgres is the finalized persistence and worker-coordination substrate for Lock Creator-granted session material is secret-bearing runtime state. It must be stored encrypted at rest in private Postgres tables using a server-side key supplied through environment/config secret. The implemented Postgres creator authority adapter supports encrypted persistence with a 32-byte key and stores only an AEAD envelope in the `creator_authorities.secret` column. Session material must never be stored in Pubky-owned resources, committed config examples, logs, readiness responses, debug formatting, error envelopes, or viewer-facing DTOs. -Postgres must not become the canonical store for Pubky-owned domain resources in the next runtime phase. Content locks, guarded resources, Lock Service Pointers, and entitlement records / verified proof bundles remain behind their existing ports. Local/dev runtime composition uses in-memory adapters; Pubky-backed adapters are implemented behind the same ports for public Locks resources and private `/priv/locks.app/` resources. Do not add `dev_*` Postgres tables for those resources. +Postgres must not become the canonical store for Pubky-owned domain resources in the next runtime phase. Content locks, guarded resources, Lock Service Pointers, and entitlement records / verified proof bundles remain behind their existing ports. Local/dev runtime composition uses in-memory adapters; Pubky-backed adapters are implemented behind the same ports for public Locks resources and private `/priv/app.locks/` resources. Do not add `dev_*` Postgres tables for those resources. Migrations are part of the runtime boundary from the first Postgres table. Schema creation must be managed by migrations rather than ad hoc test/startup SQL. @@ -135,7 +135,7 @@ Rules: ### ContentLockPath - Relative creator-homeserver path only. -- Must be exactly `/pub/locks.app/.json`. +- Must be exactly `/pub/app.locks/.json`. - Not a full Pubky URL. - Not a homeserver URL. - Not any other `/pub/...` path. @@ -177,7 +177,7 @@ Responsibilities: - Compute guarded resource hash and exact positive byte size from uploaded bytes. - Validate guarded resource MIME content type through Locks-native spec objects. - Create content locks only for currently registered guarded resource descriptors; all resources are validated all-or-nothing, at least one resource is required, and primary/secondary paths must not duplicate each other. -- Author the Lock Service Pointer for the creator default Lock Server at canonical path `/pub/locks.app/config.json`. +- Author the Lock Service Pointer for the creator default Lock Server at canonical path `/pub/app.locks/config.json`. - Require a Locks-local frontend session and derive creator identity from that session rather than trusting request-body creator fields. - Refuse creator publishing operations when the Lock Server has no valid creator-granted homeserver authority for the authenticated creator. - Keep future external-authoring mode possible, where another creator app pre-publishes guarded content and content locks while Locks only verifies, writes proof bundles, and proxy-reads through its creator-granted session. @@ -235,16 +235,16 @@ Responsibilities: - Resolve Pubky resources and Lock Server addresses. - Use one creator-granted Locks app session per creator per Lock Server, reused across that creator's locks. -- Require creator-granted capability scope for `/pub/locks.app/:rw` and `/priv/locks.app/:rw` in the production Pubky-backed flow. +- Require creator-granted capability scope for `/pub/app.locks/:rw` and `/priv/app.locks/:rw` in the production Pubky-backed flow. - Persist native session secrets for the Lock Server; current expected session lifetime is 6 months. - Own Creator Authority Acquisition: the process by which the Lock Server obtains or refreshes creator-granted homeserver authority for Locks public and private namespaces. - Treat redirect, popup, iframe, and native pubky.app rendering as presentation shells over the same acquisition state machine, not as domain/application concepts. ADR 0019 chooses Lock-Server-hosted redirect/popup for legacy-connect because the legacy Pubky authorization URL is secret-bearing; iframe and pubky.app-native rendering are deferred shells. - Implement the existing Pubky QR/deeplink auth flow as the legacy/cookie creator authorization path first, then migrate to the SDK grant flow (`PubkyGrantAuthFlow` / `GrantCredential`) as the durable production auth primitive. - Reject manual operator provisioning and direct raw-session submission as production paths for creator-granted session acquisition. - Treat creator authority status-check UX/API semantics as part of the authenticated `pubky.app/browser -> Lock Server` relationship; if exposed, derive creator from that authenticated context rather than accepting an arbitrary public key query parameter. -- Read and write public Locks app resources under `/pub/locks.app/`. -- Read and write private Locks app resources under `/priv/locks.app/`, including guarded content bytes and verified proof bundles. -- Treat private `/priv/locks.app/...` writes as non-public: they emit no public events, private paths are not visible to clients, and Locks discovery relies on public `/pub/locks.app/...` resources. +- Read and write public Locks app resources under `/pub/app.locks/`. +- Read and write private Locks app resources under `/priv/app.locks/`, including guarded content bytes and verified proof bundles. +- Treat private `/priv/app.locks/...` writes as non-public: they emit no public events, private paths are not visible to clients, and Locks discovery relies on public `/pub/app.locks/...` resources. - Revalidate creator sessions lazily before Pubky writes and proxy reads; if the SDK can refresh or revalidate an expired/refreshable session, attempt one refresh/revalidation before returning `creator_authority_unavailable`. - Provide the production replacement for today's dev/test creator publishing routes by requiring both a Locks-local frontend session for `pubky.app/browser -> Lock Server` authorization and creator-granted homeserver authority for Pubky reads/writes. - Hide unstable Pubky integration details behind ports. @@ -253,7 +253,7 @@ Responsibilities: ### Content Creator -Publishes guarded resources and content locks. Grants the Lock Server one reusable Locks app session scoped to `/pub/locks.app/:rw` and `/priv/locks.app/:rw` so the Lock Server can author public Locks resources, manage guarded Locks resources, write verified proof bundles, and proxy-read guarded content across that creator's locks. +Publishes guarded resources and content locks. Grants the Lock Server one reusable Locks app session scoped to `/pub/app.locks/:rw` and `/priv/app.locks/:rw` so the Lock Server can author public Locks resources, manage guarded Locks resources, write verified proof bundles, and proxy-read guarded content across that creator's locks. ### Content Viewer @@ -265,7 +265,7 @@ Verifies viewer proof, writes successful entitlement records to creator-owned gu ### Homeserver -In the production design, stores public lock policies, public Locks configuration, private guarded content bytes, and private verified proof bundles. Enforces Pubky sessions and path-scoped capabilities. The confirmed Locks private data namespace is `/priv/locks.app/`; Locks should depend on that homeserver capability rather than implementing a parallel guarded storage/auth namespace. The current local creator publishing implementation stores these resources in in-memory repositories instead of writing to a homeserver. +In the production design, stores public lock policies, public Locks configuration, private guarded content bytes, and private verified proof bundles. Enforces Pubky sessions and path-scoped capabilities. The confirmed Locks private data namespace is `/priv/app.locks/`; Locks should depend on that homeserver capability rather than implementing a parallel guarded storage/auth namespace. The current local creator publishing implementation stores these resources in in-memory repositories instead of writing to a homeserver. ## Aggregates, Entities, and Value Objects @@ -289,7 +289,7 @@ Invariants: - Lock hash is `BLAKE3` over the canonical JSON representation of all serialized content lock payload fields. - Lock ID is the Crockford-base32 encoding of the lock hash, without a readability prefix. - Lock ID and lock hash are derived values, not serialized fields inside the content lock payload, to avoid circular hashing. -- Public content lock path includes a `.json` extension: `/pub/locks.app/.json`. +- Public content lock path includes a `.json` extension: `/pub/app.locks/.json`. - Criteria referenced by lock logic must exist in the lock. - Guarded resource must include enough information to identify the content version: creator-relative path, guarded resource hash, MIME content type, and positive byte size. - Guarded resource `content_type` must parse as MIME. @@ -380,7 +380,7 @@ Invariants: - Stores minimal criterion-level verification result evidence, not raw proof material. - Stored under creator-owned guarded Locks storage. - Resolvable by Bundle ID. -- References the content lock using `pubky_lock_resource`, exactly `pubky/pub/locks.app/.json`. +- References the content lock using `pubky_lock_resource`, exactly `pubky/pub/app.locks/.json`. - To honor the entitlement, the Lock Server derives creator, content lock path, and Lock ID from `pubky_lock_resource`, reads the content lock, and verifies that the content lock file hashes to the embedded Lock ID. - Remains intact when a changed lock creates a new Lock ID. - Revoked by deleting the verified proof bundle. @@ -593,15 +593,15 @@ The domain model should allow verifier implementations without depending on paym ### RegisterGuardedResource -Local dev/test creator publishing use case that accepts creator, guarded resource path, MIME content type, and bytes; computes hash and positive size; validates the guarded resource descriptor; and stores/replaces the current guarded resource by creator/path. The local HTTP contract and Pubky-backed repository adapters both use `/priv/locks.app/content/` for private guarded bytes; the difference is the configured repository backend, not the path. +Local dev/test creator publishing use case that accepts creator, guarded resource path, MIME content type, and bytes; computes hash and positive size; validates the guarded resource descriptor; and stores/replaces the current guarded resource by creator/path. The local HTTP contract and Pubky-backed repository adapters both use `/priv/app.locks/content/` for private guarded bytes; the difference is the configured repository backend, not the path. ### CreateContentLock -Local dev/test creator publishing use case that verifies a registered guarded resource descriptor, builds a content lock, derives its Lock ID/content lock path, and stores the content lock through the configured repository port. Default local runtime uses in-memory storage; Pubky-backed repository tests prove the same use case writes public content locks under `/pub/locks.app/.json` when composed with Pubky homeserver adapters. +Local dev/test creator publishing use case that verifies a registered guarded resource descriptor, builds a content lock, derives its Lock ID/content lock path, and stores the content lock through the configured repository port. Default local runtime uses in-memory storage; Pubky-backed repository tests prove the same use case writes public content locks under `/pub/app.locks/.json` when composed with Pubky homeserver adapters. ### SetLockServicePointer -Local dev/test creator publishing use case that stores or replaces the creator's default Lock Service Pointer for `/pub/locks.app/config.json` through the configured repository port. Content lock creation does not require this pointer, but viewer discovery uses it when a content lock has no `lock_server.override`. +Local dev/test creator publishing use case that stores or replaces the creator's default Lock Service Pointer for `/pub/app.locks/config.json` through the configured repository port. Content lock creation does not require this pointer, but viewer discovery uses it when a content lock has no `lock_server.override`. ### CreatorAuthorityAcquisition @@ -670,7 +670,7 @@ These are conceptual events for internal organization. They do not imply Pubky ` 4. Content creator defines lock criteria and lock logic. 5. System canonicalizes the content lock. 6. System derives Lock ID from the lock hash using Crockford base32. -7. Content creator publishes public content lock under `/pub/locks.app/.json`. +7. Content creator publishes public content lock under `/pub/app.locks/.json`. 8. Content creator optionally publishes a preview post pointing to the content lock. ## Main Flow: Content Retrieval diff --git a/docs/LOCAL_OPERATOR_DEMO.md b/docs/LOCAL_OPERATOR_DEMO.md index 63bdd77..0622966 100644 --- a/docs/LOCAL_OPERATOR_DEMO.md +++ b/docs/LOCAL_OPERATOR_DEMO.md @@ -15,7 +15,9 @@ Creator publishing is authenticated. The removed unauthenticated local/dev creat ## Paykit Compose local demonstration -The repository's browser-facing Paykit demonstration is a separate operator path from the manual single-server walkthrough below. Its local-only definition is `compose.paykit-local-demo.yaml`. It composes PostgreSQL, Bitcoin regtest, Fulcrum, Pubky testnet v0.11, Locks, Paykit Server, and the creator and reader browser demos. External source builds use anonymous public Git contexts, so no sibling repository checkout is required. Paykit Server is pinned to `v0.1.0-rc2`, its compatible Locks build context is pinned to `v0.1.0-rc1`, and Paykit library contexts use `v0.1.0-rc48`. The local worktree override remains available through the exact absolute `PAYKIT_SERVER_CONTEXT` flow documented in the example README. +The repository's browser-facing Paykit demonstration is a separate operator path from the manual single-server walkthrough below. Its local-only definition is `compose.paykit-local-demo.yaml`. It composes PostgreSQL, Bitcoin regtest, Fulcrum, Pubky testnet v0.11, Locks, Paykit Server, and the creator and reader browser demos. Paykit Server is pinned to merged commit `26bda476b9fa1d29feb87cbb24a90042d00f42c4` and built against the current Locks worktree so both services use the same protocol paths; Paykit library contexts use `v0.1.0-rc48`. An absolute local `PAYKIT_SERVER_CONTEXT` remains available as an optional coordinated-development override. + +The `app.locks` path change does not migrate old demo resources or grants. Republish old locks and reauthorize creator identities for the new public and private namespaces. If disposable demo database state still references `locks.app`, finish or discard that state before restarting on this version; do not delete persistent volumes as a routine upgrade step. The Paykit iframe remains Paykit-owned and presents the production Bitkit QR/deep-link path without local helper instructions. The host wrapper is only a controlled local fallback: its helper comes from the Paykit local-demo image/runtime stage and is not part of the normal production package/runtime. Operators must manually obtain the local-only bearer URL from the labeled Paykit Server log event and follow the secret-handling guidance in [`examples/js-sdk/README.md`](../examples/js-sdk/README.md). diff --git a/docs/SDK.md b/docs/SDK.md index 78a0097..21f8533 100644 --- a/docs/SDK.md +++ b/docs/SDK.md @@ -150,7 +150,7 @@ This remains the stable browser path. Creators publish their current default Lock Server at: ```text -/pub/locks.app/config.json +/pub/app.locks/config.json ``` Expected pointer shape: @@ -181,7 +181,7 @@ const locksFromPointer = Locks.fromCreatorLockServicePointer(pointer); const locksFromCreator = await Locks.forCreator("pubky..."); ``` -`Locks.forCreator` performs a browser PKARR/domain lookup for the creator homeserver, fetches `/pub/locks.app/config.json`, validates the pointer JSON, then uses the discovered `default_lock_server`. It does not use a gateway/base URL fallback. +`Locks.forCreator` performs a browser PKARR/domain lookup for the creator homeserver, fetches `/pub/app.locks/config.json`, validates the pointer JSON, then uses the discovered `default_lock_server`. It does not use a gateway/base URL fallback. ## Connect flow @@ -268,7 +268,7 @@ import { RegisterGuardedResourceOptions } from "locks-sdk-wasm"; const bytes = new TextEncoder().encode("guarded bytes"); const registered = await session.creator.registerGuardedResource( new RegisterGuardedResourceOptions( - "example.txt", // relative path under /priv/locks.app/content/ + "example.txt", // relative path under /priv/app.locks/content/ "text/plain", bytes, ), @@ -285,7 +285,7 @@ Content-Type: text/plain guarded bytes ``` -Callers supply only the relative content path, not the full `/priv/locks.app/content/` path. The request body is raw bytes (`Uint8Array` in JS/WASM), not JSON/base64. +Callers supply only the relative content path, not the full `/priv/app.locks/content/` path. The request body is raw bytes (`Uint8Array` in JS/WASM), not JSON/base64. ### Create content lock @@ -387,17 +387,17 @@ Viewer apps can read public lock policy JSON from a canonical Pubky lock resourc ```ts const contentLock = await Locks.readContentLock( - "pubky.../pub/locks.app/.json", + "pubky.../pub/app.locks/.json", ); const locksForContent = await Locks.forContentLock( - "pubky.../pub/locks.app/.json", + "pubky.../pub/app.locks/.json", ); ``` `readContentLock` performs a browser PKARR/domain lookup for the creator homeserver, fetches the public content lock JSON without auth, validates that the returned content lock matches the requested resource, and returns the validated JSON. It does not solve lock criteria or submit proofs. -`forContentLock` performs the same content lock read, then selects the Lock Server: it prefers the content lock's per-lock `lock_server.override`; if absent, it discovers the creator's default Lock Server through `/pub/locks.app/config.json`. +`forContentLock` performs the same content lock read, then selects the Lock Server: it prefers the content lock's per-lock `lock_server.override`; if absent, it discovers the creator's default Lock Server through `/pub/app.locks/config.json`. ### Submit proof bundle @@ -411,7 +411,7 @@ const creator = "pubky..."; const lifecycle = await viewer.submitProofBundle({ version: 1, bundle_id: bundleId, - pubky_lock_resource: `${creator}/pub/locks.app/.json`, + pubky_lock_resource: `${creator}/pub/app.locks/.json`, proofs: [ { criterion_id: "criterion-1", diff --git a/docs/SDK_LIVE_SMOKE.md b/docs/SDK_LIVE_SMOKE.md index 2b1c586..d33aa33 100644 --- a/docs/SDK_LIVE_SMOKE.md +++ b/docs/SDK_LIVE_SMOKE.md @@ -18,8 +18,8 @@ The command is informational and exits 0. It reports whether the following envir | --- | --- | | `LOCKS_LIVE_LOCK_SERVER` | Lock Server Pubky with a browser-usable PKARR endpoint | | `LOCKS_LIVE_PKARR_RELAY` | PKARR relay URL. Local `pubky-testnet` uses `http://127.0.0.1:15411` | -| `LOCKS_LIVE_CREATOR` | Creator Pubky publishing `/pub/locks.app/config.json` | -| `LOCKS_LIVE_CONTENT_LOCK_RESOURCE` | Canonical `pubky.../pub/locks.app/.json` resource | +| `LOCKS_LIVE_CREATOR` | Creator Pubky publishing `/pub/app.locks/config.json` | +| `LOCKS_LIVE_CONTENT_LOCK_RESOURCE` | Canonical `pubky.../pub/app.locks/.json` resource | | `LOCKS_LIVE_DEMO_ORIGIN` | Browser origin allowed by `creator_authority_acquisition.legacy_connect.allowed_return_origins` | ## Manual smoke sequence @@ -51,7 +51,7 @@ Then verify: 4. The connect callback path can parse `code` and `state`, validate caller-managed state, and exchange the code for a frontend session. 5. `session.exportSecret()` and `locks.restoreSession(secret)` round-trip the session and retain Lock Server context. 6. `session.signout()` revokes the current frontend session. -7. `Locks.forCreatorWithOptions(creator, options)` resolves creator PKARR, fetches `/pub/locks.app/config.json`, validates it, and selects the creator default Lock Server. +7. `Locks.forCreatorWithOptions(creator, options)` resolves creator PKARR, fetches `/pub/app.locks/config.json`, validates it, and selects the creator default Lock Server. 8. `Locks.readContentLockWithOptions(resource, options)` resolves the creator homeserver, fetches the public content lock, validates that it matches the requested resource, and returns JSON. 9. `Locks.forContentLockWithOptions(resource, options)` selects the Lock Server from per-lock override or creator pointer fallback. 10. `viewer.submitProofBundle(...)`, `lookupVerificationTask`, `issueAccessCredential`, and `proxyReadGuardedResource` work for a known satisfiable proof bundle. diff --git a/docs/THESAURUS.md b/docs/THESAURUS.md index 29e684b..9f748d3 100644 --- a/docs/THESAURUS.md +++ b/docs/THESAURUS.md @@ -67,7 +67,7 @@ Repository/workspace structure, protocol payload ownership, and code-boundary la - **Related terms**: Content Lock, Creator Publishing, Guarded Resource Registration, Lock ID ### Lock ID -- **Definition**: Identifier for a content lock, encoded by the Rust `base32` crate as fixed-length 52-character Crockford base32 of the full 32-byte BLAKE3 lock hash with no prefix or checksum; canonical form is uppercase and used in `/pub/locks.app/.json` so changing the lock creates a new content lock file. +- **Definition**: Identifier for a content lock, encoded by the Rust `base32` crate as fixed-length 52-character Crockford base32 of the full 32-byte BLAKE3 lock hash with no prefix or checksum; canonical form is uppercase and used in `/pub/app.locks/.json` so changing the lock creates a new content lock file. - **NOT**: A mutable arbitrary label or bearer secret. - **Synonyms to AVOID**: lock name, policy id, file id - **Related terms**: Content Lock, Lock Hash @@ -80,13 +80,13 @@ Repository/workspace structure, protocol payload ownership, and code-boundary la ### Pubky Lock Resource -- **Definition**: Protocol-facing addressed Pubky resource for a public content lock, exactly `pubky/pub/locks.app/.json`, matching the preferred `PubkyResource` form from the `pubky` crate. It derives creator, content lock path, and Lock ID. +- **Definition**: Protocol-facing addressed Pubky resource for a public content lock, exactly `pubky/pub/app.locks/.json`, matching the preferred `PubkyResource` form from the `pubky` crate. It derives creator, content lock path, and Lock ID. - **NOT**: `pubky://...`, an HTTP(S) transport URL, a creator-relative path alone, or a guarded resource path. - **Synonyms to AVOID**: content lock URL, homeserver URL, lock URL - **Related terms**: Content Lock Path, Lock ID, Creator Pubky ### Content Lock Path -- **Definition**: Canonical creator-homeserver-relative public path to a content lock file, exactly `/pub/locks.app/.json`; the embedded Lock ID must parse and match the content lock file hash before an entitlement is honored. +- **Definition**: Canonical creator-homeserver-relative public path to a content lock file, exactly `/pub/app.locks/.json`; the embedded Lock ID must parse and match the content lock file hash before an entitlement is honored. - **NOT**: The content lock payload itself, a guarded path, full Pubky URL, homeserver URL, or another `/pub/...` path. - **Synonyms to AVOID**: lock path, lock file path, policy path - **Related terms**: Content Lock, Lock ID, Public Lock Path, Verified Proof Bundle @@ -116,7 +116,7 @@ Repository/workspace structure, protocol payload ownership, and code-boundary la - **Related terms**: Guarded Content, Guarded Resource Hash, Guarded Resource Registration, Proxy Read ### Guarded Resource Registration -- **Definition**: Authenticated creator publishing action exposed as `PUT /creator/priv-resources/content/`; it stores or replaces current guarded bytes and metadata for the session-derived creator and canonical guarded path under `/priv/locks.app/content/`, then returns a guarded resource descriptor. +- **Definition**: Authenticated creator publishing action exposed as `PUT /creator/priv-resources/content/`; it stores or replaces current guarded bytes and metadata for the session-derived creator and canonical guarded path under `/priv/app.locks/content/`, then returns a guarded resource descriptor. - **NOT**: A creator authorization proof, public content lock, or viewer proxy-read. - **Synonyms to AVOID**: seed endpoint, locked resource upload - **Related terms**: Guarded Resource, Content Creator, Creator Publishing, Guarded Path @@ -134,7 +134,7 @@ Repository/workspace structure, protocol payload ownership, and code-boundary la - **Related terms**: Guarded Resource, Lock Hash, Entitlement Record ### Lock Service Pointer -- **Definition**: Creator-owned public configuration at `/pub/locks.app/config.json` that tells viewers which Lock Server to use by default when a content lock does not specify `lock_server.override`; authored through authenticated `POST /creator/lock-service-config`. +- **Definition**: Creator-owned public configuration at `/pub/app.locks/config.json` that tells viewers which Lock Server to use by default when a content lock does not specify `lock_server.override`; authored through authenticated `POST /creator/lock-service-config`. - **NOT**: A PKDNS/Pkarr record, the per-lock Lock Server override itself, production creator authorization, or a content lock. - **Synonyms to AVOID**: service url, server config, migration pointer, _locks record - **Related terms**: Lock Server, Credible Exit, Creator Publishing diff --git a/examples/js-sdk/README.md b/examples/js-sdk/README.md index 646a8ac..9aef775 100644 --- a/examples/js-sdk/README.md +++ b/examples/js-sdk/README.md @@ -211,20 +211,11 @@ npm --prefix examples/js-sdk run authenticate-paykit -- --role content-creator Do not wrap these commands in `docker compose exec`. The host wrappers load private role state locally and bridge only bounded helper input into the relevant container. -The Paykit Server build context uses the immutable `v0.1.0-rc2` tag, its compatible -Locks context uses `v0.1.0-rc1`, Paykit Rust uses `v0.1.0-rc48`, and Pubky Homeserver uses -`v0.11.0`. The active Locks checkout is used only for the Locks and browser-demo -images being developed. No sibling repository checkout is required. - -For coordinated pre-merge Paykit Server work, select an explicit absolute local worktree -without changing the committed public default: - -```bash -PAYKIT_SERVER_CONTEXT=/absolute/path/to/paykit-server \ - docker compose --file compose.paykit-local-demo.yaml up -d --build -``` - -Compose validation requires the rendered context to match that environment value exactly. +The Paykit Server build uses merged commit +`26bda476b9fa1d29feb87cbb24a90042d00f42c4`, the active Locks checkout, Paykit Rust +`v0.1.0-rc48`, and Pubky Homeserver `v0.11.0`. For coordinated Paykit development, +export an absolute `PAYKIT_SERVER_CONTEXT` worktree path before running Compose; +validation requires the rendered context to match that override exactly. `compose.paykit-local-demo.yaml` is intentionally limited to local development and demonstration. When `.local` is absent, the one-shot `compose-bootstrap` service creates the ignored owner-only credentials and non-state configuration before dependent services start. Existing generated credentials are validated and reused. For a quiet configuration check without printing generated environment values, run `npm --prefix examples/js-sdk run validate:paykit-compose`; the wrapper inspects a captured `docker compose --file compose.paykit-local-demo.yaml config --no-env-resolution` model. @@ -379,7 +370,7 @@ Rules: - file upload only - guarded path prefix is fixed: ```text - /priv/locks.app/content/ + /priv/app.locks/content/ ``` - only the filename segment is editable - `/` in filename is rejected @@ -449,7 +440,7 @@ The Node demo server does not write Locks resources directly to the homeserver. After success, the page displays the **Viewer content lock resource**: ```text -/pub/locks.app/.json +/pub/app.locks/.json ``` ## Reader browser flow diff --git a/examples/js-sdk/app-iframe.js b/examples/js-sdk/app-iframe.js index a42b415..74607aa 100644 --- a/examples/js-sdk/app-iframe.js +++ b/examples/js-sdk/app-iframe.js @@ -746,7 +746,7 @@ function renderSelectedResources() { function appendSelectedResource(kind, path, file) { const item = document.createElement('li'); - item.textContent = `${kind}: /priv/locks.app/content/${path} (${file.name}, ${file.size} bytes)`; + item.textContent = `${kind}: /priv/app.locks/content/${path} (${file.name}, ${file.size} bytes)`; el.selectedResourceList.append(item); } diff --git a/examples/js-sdk/iframe.html b/examples/js-sdk/iframe.html index fd90d50..4d15c91 100644 --- a/examples/js-sdk/iframe.html +++ b/examples/js-sdk/iframe.html @@ -65,7 +65,7 @@

3. Creator publishing