Skip to content

feat(registry): persist pnpr users and tokens to disk#11977

Merged
zkochan merged 2 commits into
mainfrom
feat/11974
May 27, 2026
Merged

feat(registry): persist pnpr users and tokens to disk#11977
zkochan merged 2 commits into
mainfrom
feat/11974

Conversation

@zkochan

@zkochan zkochan commented May 26, 2026

Copy link
Copy Markdown
Member

Summary

Backs pnpr's user and token stores with on-disk persistence so an operator can run pnpr as a hosted registry without losing every account on the next container redeploy. Closes #11974.

  • UserStore ↔ verdaccio-shaped htpasswd file (bcrypt $2y$10$…, atomically rewritten on every adduser; verifiable by Apache's htpasswd -v).
  • TokenStore ↔ SQLite database storing SHA-256 hashes of bearer tokens (raw tokens never hit disk) plus the per-record fields the upcoming /-/npm/v1/tokens surface will need (created_at, last_used_at, readonly, cidr_whitelist).
  • New YAML wiring under the existing auth: block: auth.htpasswd.{file,max_users} and auth.tokens.file (defaults to a tokens.db sibling of the htpasswd file).
  • auth.htpasswd.max_users: -1 disables registration end-to-end (returns 403).
  • Crash-safe writes: tmp + rename, same pattern as the tarball cache.
  • Both files are loaded eagerly on startup; a malformed htpasswd fails the boot rather than silently locking every existing user out.

API changes

  • pnpm_registry::router(config) stays in-memory (test-only convenience).
  • New pnpm_registry::router_with_auth(config, auth) for callers that pre-load an AuthState.
  • serve(config) does AuthState::load(&config.auth)? before binding the listener.

Test plan

  • cargo test -p pnpm-registry — 70 tests (14 unit + 26 auth_publish + 9 registry_mock + 17 server + 4 new auth_persistence) all pass.
  • cargo clippy -p pnpm-registry --all-targets -- --deny warnings clean.
  • cargo fmt --check -p pnpm-registry clean.
  • New auth_persistence integration tests cover the three acceptance criteria from the issue: restart-survival, corrupted-file diagnostic, and cross-tool compat with Apache htpasswd -v -b.

Written by an agent (Claude Code, claude-opus-4-7).

Summary by CodeRabbit

  • New Features

    • Added bcrypt-hashed user authentication and htpasswd-style credential support
    • Added bearer token authentication with SHA-256-backed persistent storage option
    • Configurable auth via YAML including token/htpasswd locations and user-registration limits
    • Server exposes an authenticated router and runtime auth state
  • Tests

    • Added acceptance tests for auth persistence, startup validation, cross-tool compatibility, and max-user behavior

Review Change Stack

Backs UserStore with a verdaccio-shaped htpasswd file (bcrypt $2y$
hashes, atomically rewritten on every adduser) and TokenStore with a
SQLite database that stores SHA-256 token hashes plus the per-record
fields the upcoming /-/npm/v1/tokens surface will need (created_at,
last_used_at, readonly, cidr_whitelist).

Configuration mirrors verdaccio's auth.htpasswd.{file,max_users}
under the existing YAML schema; tokens default to a tokens.db
sibling of htpasswd, overridable via auth.tokens.file. max_users=-1
disables registration end-to-end. Both files are written via
tmp+rename and loaded eagerly on startup so a malformed htpasswd
fails fast rather than booting with a silent empty user list.

Closes #11974.
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 790b5caf-1ec2-4a7a-9fe1-70a746c69f45

📥 Commits

Reviewing files that changed from the base of the PR and between 4df58f9 and 5970d48.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • Cargo.toml
  • registry/crates/pnpm-registry/Cargo.toml
  • registry/crates/pnpm-registry/src/auth.rs
  • registry/crates/pnpm-registry/src/config.rs
  • registry/crates/pnpm-registry/src/lib.rs
✅ Files skipped from review due to trivial changes (1)
  • registry/crates/pnpm-registry/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (2)
  • Cargo.toml
  • registry/crates/pnpm-registry/src/config.rs
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Code Coverage
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Dylint
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
registry/crates/**/*.rs

📄 CodeRabbit inference engine (registry/AGENTS.md)

Follow the pacquet code-style guide (../pacquet/CODE_STYLE_GUIDE.md) for Rust-level conventions including imports, naming, ownership, error handling, and test layout

Files:

  • registry/crates/pnpm-registry/src/lib.rs
  • registry/crates/pnpm-registry/src/auth.rs
🔇 Additional comments (2)
registry/crates/pnpm-registry/src/lib.rs (1)

22-29: LGTM!

registry/crates/pnpm-registry/src/auth.rs (1)

543-552: LGTM!


📝 Walkthrough

Walkthrough

Adds persistent auth to pnpm-registry: bcrypt-hashed htpasswd user storage and SQLite-backed SHA-256 token hashes, new auth config parsing, server wiring to load/persist auth state, expanded errors/dependencies, and acceptance tests for persistence, corruption diagnostics, compatibility, and registration policy.

Changes

Persistent Authentication for pnpm-registry

Layer / File(s) Summary
Dependencies, errors, and public API
Cargo.toml, registry/crates/pnpm-registry/Cargo.toml, registry/crates/pnpm-registry/src/error.rs, registry/crates/pnpm-registry/src/lib.rs
Adds workspace deps (bcrypt, getrandom) and crate deps (bcrypt, rusqlite); extends RegistryError with registration/htpasswd/backend variants; re-exports auth types and router_with_auth.
Authentication configuration schema
registry/crates/pnpm-registry/src/config.rs
Introduces AuthConfig, HtpasswdConfig, TokensConfig, MaxUsers; parses auth YAML block, resolves relative paths, derives default tokens.db sibling, and adds unit tests for path/sentinel behaviors.
Core authentication: bcrypt and persistence
registry/crates/pnpm-registry/src/auth.rs
Adds AuthState bundling UserStore and TokenStore; UserStore uses bcrypt-hashed htpasswd files with atomic writes and max-user enforcement; TokenStore stores only SHA-256 token hashes with optional SQLite persistence and in-memory cache; strict htpasswd parsing and crypto helpers included; tests updated.
Server integration of authentication
registry/crates/pnpm-registry/src/server.rs
Adds router_with_auth(config, auth); router(config) delegates to in-memory AuthState; serve(config) loads persisted auth via AuthState::load(); handlers and enforce_access use async auth APIs and identify() with the new stores.
Acceptance tests for persistence
registry/crates/pnpm-registry/tests/auth_persistence.rs
Adds helper utilities and four tokio::test cases verifying restart persistence, malformed htpasswd diagnostics, cross-tool htpasswd -v compatibility, and max_users policy enforcement returning 403 when disabled.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Serve as serve(config)
  participant AuthState as AuthState::load
  participant UserStore as UserStore::open
  participant TokenStore as TokenStore::open
  participant Htpasswd as Htpasswd File
  participant SQLite as SQLite DB
  participant Handler as add_or_login Handler

  Note over Serve,AuthState: Startup with persisted auth
  Serve->>AuthState: load(config.auth)
  AuthState->>UserStore: open(htpasswd_path)
  UserStore->>Htpasswd: read & parse htpasswd (bcrypt)
  Htpasswd-->>UserStore: bcrypt hashes loaded
  AuthState->>TokenStore: open(tokens_db_path)
  TokenStore->>SQLite: init schema + load all token records
  SQLite-->>TokenStore: token hashes + metadata
  TokenStore-->>AuthState: in-memory cache ready
  AuthState-->>Serve: AuthState ready

  Note over Client,Handler: New user registration flow
  Client->>Handler: PUT /-/user/org.couchdb.user:newuser
  Handler->>UserStore: add_or_login (async)
  UserStore->>UserStore: spawn_blocking bcrypt hash
  UserStore->>UserStore: enforce MaxUsers under mutex
  UserStore->>Htpasswd: persist htpasswd atomically
  UserStore-->>Handler: Created
  Handler->>TokenStore: issue token (async)
  TokenStore->>TokenStore: mint raw token, hash SHA-256
  TokenStore->>SQLite: upsert hash -> TokenRecord
  SQLite-->>TokenStore: persisted
  TokenStore-->>Handler: raw token returned
  Handler-->>Client: 201 + token

  Note over Client,Handler: After restart, token lookup
  Client->>Serve: Request with Authorization: Bearer <token>
  Handler->>TokenStore: lookup(raw_token)
  TokenStore->>TokenStore: hash raw token, consult in-memory map
  TokenStore-->>Handler: username
  Handler-->>Client: 200 + data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • pnpm/pnpm#11914: Another PR touching pnpm-registry auth and identify wiring; relates to earlier in-memory/plaintext auth changes.
  • pnpm/pnpm#11970: Changes to config.rs and registry server wiring; related to configuration parsing and runtime initialization.

Suggested reviewers

  • KSXGitHub

Poem

🐰 A rabbit hums beside the keys,

Bcrypt hops in winter breeze,
Htpasswd files snug and tight,
Tokens hashed through day and night,
Restarts find the stored delight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(registry): persist pnpr users and tokens to disk' directly and accurately describes the main change: implementing persistent storage for user and token data.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from #11974: persistent htpasswd with bcrypt hashing, SQLite token storage with SHA-256 hashes, config support for auth paths and max_users, crash-safe writes, and comprehensive integration tests.
Out of Scope Changes check ✅ Passed All changes are scoped to the linked issue #11974: auth persistence, config parsing, error handling, and integration tests. No unrelated modifications were introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/11974

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Persist pnpr users and tokens to disk for hosted registry durability

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Persist user accounts to htpasswd file with bcrypt hashing
• Store bearer tokens in SQLite database with SHA-256 hashing
• Add YAML configuration for auth file paths and user registration caps
• Implement crash-safe atomic writes and eager startup validation
• Support Apache htpasswd tool compatibility for cross-tool verification
Diagram
flowchart LR
  Config["Config with auth paths"]
  AuthState["AuthState::load"]
  UserStore["UserStore<br/>htpasswd file"]
  TokenStore["TokenStore<br/>SQLite DB"]
  AddUser["adduser endpoint"]
  Verify["Bearer/Basic auth"]
  
  Config -->|"parse auth block"| AuthState
  AuthState -->|"open file"| UserStore
  AuthState -->|"open DB"| TokenStore
  AddUser -->|"bcrypt hash"| UserStore
  AddUser -->|"SHA-256 hash"| TokenStore
  Verify -->|"lookup token"| TokenStore
  Verify -->|"verify password"| UserStore

Loading

Grey Divider

File Changes

1. registry/crates/pnpm-registry/src/auth.rs ✨ Enhancement +650/-150

Implement file-backed user and token persistence with bcrypt

• Replaced in-memory plaintext password store with bcrypt-hashed htpasswd file persistence
• Replaced in-memory token map with SQLite-backed store that persists SHA-256 token hashes
• Added AuthState bundle to manage both stores with eager loading on startup
• Implemented atomic file writes via tmp+rename pattern for crash safety
• Added htpasswd parsing/serialization with Apache format compatibility
• Converted add_or_login and issue to async for bcrypt/SQLite blocking operations
• Added registration cap enforcement (max_users limit and disable modes)

registry/crates/pnpm-registry/src/auth.rs


2. registry/crates/pnpm-registry/src/config.rs ⚙️ Configuration changes +190/-0

Add YAML configuration for auth file paths and user caps

• Added AuthConfig, HtpasswdConfig, TokensConfig structs for YAML auth block
• Added MaxUsers enum to represent three-state registration cap (unlimited/disabled/limited)
• Implemented YAML deserialization for auth.htpasswd.{file,max_users} and auth.tokens.file
• Added path resolution logic to make paths relative to config directory
• Implemented default tokens.db sibling path when tokens file is unset
• Added comprehensive config tests for auth block parsing and path resolution

registry/crates/pnpm-registry/src/config.rs


3. registry/crates/pnpm-registry/src/error.rs Error handling +44/-0

Add error types for auth persistence and validation

• Added RegistrationDisabled error for max_users: -1 blocking new signups
• Added TooManyUsers error for registration cap enforcement
• Added InvalidHtpasswdFile error with diagnostic details for startup validation
• Added Bcrypt, Sqlite, and JoinError variants for async operation failures
• Updated status code mapping to return 403 for registration errors and 500 for startup errors

registry/crates/pnpm-registry/src/error.rs


View more (5)
4. registry/crates/pnpm-registry/src/server.rs ✨ Enhancement +28/-20

Wire AuthState into router and serve functions

• Replaced separate users and tokens fields with unified auth: AuthState
• Added router_with_auth function to accept pre-loaded AuthState for testing
• Updated router to call router_with_auth with in-memory auth for backward compatibility
• Modified serve to load AuthState from config before binding socket
• Updated add_user handler to await async add_or_login and issue calls
• Updated enforce_access to reference auth stores through AuthState

registry/crates/pnpm-registry/src/server.rs


5. registry/crates/pnpm-registry/src/lib.rs ✨ Enhancement +6/-2

Export auth types and router_with_auth function

• Exported AuthState, UserStore, TokenStore types for public API
• Exported auth config types (AuthConfig, HtpasswdConfig, TokensConfig, MaxUsers)
• Exported new router_with_auth function for callers with pre-loaded auth state

registry/crates/pnpm-registry/src/lib.rs


6. registry/crates/pnpm-registry/tests/auth_persistence.rs 🧪 Tests +230/-0

Add acceptance tests for auth persistence and validation

• Added integration test for user and token survival across process restart
• Added test for corrupt htpasswd file detection at startup with diagnostic message
• Added cross-tool compatibility test verifying Apache htpasswd -v can verify pnpr-written hashes
• Added end-to-end test for max_users: -1 registration disable policy

registry/crates/pnpm-registry/tests/auth_persistence.rs


7. Cargo.toml Dependencies +1/-0

Add bcrypt dependency

• Added bcrypt workspace dependency for password hashing

Cargo.toml


8. registry/crates/pnpm-registry/Cargo.toml Dependencies +2/-0

Add bcrypt and rusqlite dependencies

• Added bcrypt dependency for password hashing
• Added rusqlite dependency for SQLite token database

registry/crates/pnpm-registry/Cargo.toml


Grey Divider

Qodo Logo

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.01      7.8±0.17ms   559.7 KB/sec    1.00      7.7±0.05ms   564.8 KB/sec

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
registry/crates/pnpm-registry/tests/auth_persistence.rs (1)

138-142: 💤 Low value

TempDir dropped immediately — inconsistent with other tests.

The inline TempDir::new().unwrap() is dropped at the end of this expression, deleting the directory before the test proceeds. The test still passes because AuthState::load only reads auth paths (htpasswd/tokens), not storage. For consistency with other tests and to avoid mysterious failures if test logic evolves:

♻️ Bind TempDir to preserve lifetime
+    let storage = TempDir::new().unwrap();
     let config = persistent_config(
-        TempDir::new().unwrap().path().to_path_buf(),
+        storage.path().to_path_buf(),
         htpasswd.clone(),
         auth_dir.path().join("tokens.db"),
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@registry/crates/pnpm-registry/tests/auth_persistence.rs` around lines 138 -
142, The TempDir created inline is dropped immediately; bind it to a local
variable to preserve the directory for the duration of the test. Replace the
inline TempDir::new().unwrap() used in persistent_config with a stored value
(e.g., let temp_dir = TempDir::new().unwrap();) and pass
temp_dir.path().to_path_buf() into persistent_config so the temporary directory
remains alive while the test (and functions like AuthState::load) run.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@registry/crates/pnpm-registry/src/auth.rs`:
- Around line 543-560: The fresh_secret function currently derives a secret from
time/pid/stack addresses; replace its implementation to use an OS-backed CSPRNG
(e.g., add getrandom or rand with rand::rngs::OsRng to Cargo.toml) and fill the
32-byte array from the OS RNG instead of hashing deterministic inputs; update
fresh_secret to call getrandom (or OsRng) to populate the [u8;32] and
handle/report any error (propagate or unwrap with clear message) so
TokenStore::issue receives a cryptographically strong secret.

In `@registry/crates/pnpm-registry/src/config.rs`:
- Around line 93-96: The docs claim that auth.htpasswd.max_users accepts “+inf”
but HtpasswdFile.max_users is deserialized as Option<i64> and then converted
with MaxUsers::from_yaml(i64), so YAML infinity tokens will fail; either remove
“+inf” from the docs or implement custom deserialization to treat YAML
+inf/+.inf as MaxUsers::Unlimited. To fix in code, add a custom Deserialize impl
or a serde with deserialize_with for HtpasswdFile.max_users that accepts integer
values into Option<i64> and maps floating-point infinity tokens (positive
infinity) to Some(MaxUsers::Unlimited) via MaxUsers::from_yaml (or adjust
MaxUsers to accept a sentinel), update MaxUsers::from_yaml to handle the
sentinel if needed, and add a unit test that deserializes
auth.htpasswd.max_users: +inf to assert it becomes Unlimited; alternatively
update the documentation string to drop “+inf” if you don’t implement the
deserializer.

---

Nitpick comments:
In `@registry/crates/pnpm-registry/tests/auth_persistence.rs`:
- Around line 138-142: The TempDir created inline is dropped immediately; bind
it to a local variable to preserve the directory for the duration of the test.
Replace the inline TempDir::new().unwrap() used in persistent_config with a
stored value (e.g., let temp_dir = TempDir::new().unwrap();) and pass
temp_dir.path().to_path_buf() into persistent_config so the temporary directory
remains alive while the test (and functions like AuthState::load) run.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 97d3bb51-c3c6-47f8-8dfe-9c1ab8e6d50c

📥 Commits

Reviewing files that changed from the base of the PR and between c12681f and 4df58f9.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml
  • registry/crates/pnpm-registry/Cargo.toml
  • registry/crates/pnpm-registry/src/auth.rs
  • registry/crates/pnpm-registry/src/config.rs
  • registry/crates/pnpm-registry/src/error.rs
  • registry/crates/pnpm-registry/src/lib.rs
  • registry/crates/pnpm-registry/src/server.rs
  • registry/crates/pnpm-registry/tests/auth_persistence.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Code Coverage
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (3)
registry/crates/**/Cargo.toml

📄 CodeRabbit inference engine (registry/AGENTS.md)

registry/crates/**/Cargo.toml: When using existing pacquet-* crates, declare them in the root [workspace.dependencies] and use { workspace = true } in the crate's Cargo.toml
New registry-only crates must be placed under registry/crates// and named pnpm-registry- in Cargo.toml, with the package name using the pnpm-registry- prefix exclusively
Do not reach for the pacquet- prefix to name new registry-side crates; use pnpm-registry- prefix exclusively for registry-only crates

Files:

  • registry/crates/pnpm-registry/Cargo.toml
registry/**/Cargo.toml

📄 CodeRabbit inference engine (registry/AGENTS.md)

Add new registry-only crates to [workspace.dependencies] at the root with the pnpm-registry- prefix so other crates can use { workspace = true }

Files:

  • registry/crates/pnpm-registry/Cargo.toml
registry/crates/**/*.rs

📄 CodeRabbit inference engine (registry/AGENTS.md)

Follow the pacquet code-style guide (../pacquet/CODE_STYLE_GUIDE.md) for Rust-level conventions including imports, naming, ownership, error handling, and test layout

Files:

  • registry/crates/pnpm-registry/src/lib.rs
  • registry/crates/pnpm-registry/src/error.rs
  • registry/crates/pnpm-registry/src/server.rs
  • registry/crates/pnpm-registry/tests/auth_persistence.rs
  • registry/crates/pnpm-registry/src/config.rs
  • registry/crates/pnpm-registry/src/auth.rs
🔇 Additional comments (21)
Cargo.toml (1)

85-85: LGTM!

registry/crates/pnpm-registry/Cargo.toml (1)

25-25: LGTM!

Also applies to: 32-32

registry/crates/pnpm-registry/src/error.rs (1)

90-125: LGTM!

Also applies to: 169-175

registry/crates/pnpm-registry/src/lib.rs (1)

22-26: LGTM!

Also applies to: 29-29

registry/crates/pnpm-registry/src/config.rs (1)

58-92: LGTM!

Also applies to: 97-162, 168-337, 591-663

registry/crates/pnpm-registry/src/auth.rs (7)

1-66: LGTM!


68-177: LGTM!


179-324: LGTM!


356-441: LGTM!


443-466: LGTM!


468-537: LGTM!


591-814: LGTM!

registry/crates/pnpm-registry/src/server.rs (5)

13-13: LGTM!

Also applies to: 40-53


55-99: LGTM!


101-113: LGTM!


485-527: LGTM!


1035-1060: LGTM!

registry/crates/pnpm-registry/tests/auth_persistence.rs (4)

1-58: LGTM!


60-126: LGTM!


151-204: LGTM!


206-230: LGTM!

Comment thread registry/crates/pnpm-registry/src/auth.rs
Comment thread registry/crates/pnpm-registry/src/config.rs Outdated
@codecov-commenter

codecov-commenter commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.18072% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.14%. Comparing base (f03dc2d) to head (5970d48).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
registry/crates/pnpm-registry/src/auth.rs 94.94% 20 Missing ⚠️
registry/crates/pnpm-registry/src/server.rs 75.00% 3 Missing ⚠️
registry/crates/pnpm-registry/src/error.rs 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #11977      +/-   ##
==========================================
+ Coverage   88.03%   88.14%   +0.10%     
==========================================
  Files         228      228              
  Lines       28068    28480     +412     
==========================================
+ Hits        24711    25103     +392     
- Misses       3357     3377      +20     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Scenario: Isolated linker: fresh restore, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.968 ± 0.093 1.844 2.145 1.00
pacquet@main 1.974 ± 0.067 1.868 2.113 1.00 ± 0.06
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.96777778192,
      "stddev": 0.09342484875771102,
      "median": 1.9580025243199999,
      "user": 2.80142354,
      "system": 3.286039,
      "min": 1.8435277543200002,
      "max": 2.14547043032,
      "times": [
        1.9115061003200002,
        1.9048591703200002,
        1.8435277543200002,
        2.0276237623199997,
        1.9134456783200002,
        2.0440368043199997,
        1.87321355332,
        2.14547043032,
        2.01153519532,
        2.0025593703199998
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.9742864198199999,
      "stddev": 0.06746030933602035,
      "median": 1.96912252582,
      "user": 2.7897805399999998,
      "system": 3.3093415999999998,
      "min": 1.8675589253200002,
      "max": 2.11317951232,
      "times": [
        1.8675589253200002,
        1.9336737543200002,
        2.01017555032,
        1.94166034432,
        1.9831852413200002,
        1.92267856732,
        2.02916954132,
        1.95505981032,
        1.9865229513200002,
        2.11317951232
      ]
    }
  ]
}

Scenario: Isolated linker: fresh restore, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 650.1 ± 22.4 633.0 709.5 1.02 ± 0.04
pacquet@main 638.4 ± 11.8 623.5 667.7 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.6500932656600001,
      "stddev": 0.02241370768005456,
      "median": 0.6424916378600001,
      "user": 0.36488186,
      "system": 1.3515652399999998,
      "min": 0.6330469173600001,
      "max": 0.7095458213600001,
      "times": [
        0.7095458213600001,
        0.6609341913600001,
        0.6330469173600001,
        0.6373782973600001,
        0.6407561553600001,
        0.6425668673600001,
        0.6441161353600001,
        0.63707491236,
        0.65309695036,
        0.64241640836
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.6384122295600001,
      "stddev": 0.011830419637898609,
      "median": 0.63889951686,
      "user": 0.37200346000000006,
      "system": 1.3353949399999998,
      "min": 0.6235060463600001,
      "max": 0.6676556623600001,
      "times": [
        0.6676556623600001,
        0.6235060463600001,
        0.6404469593600001,
        0.6386655773600001,
        0.6391334563600001,
        0.6311087843600001,
        0.63099336636,
        0.6394464403600001,
        0.64212334136,
        0.6310426613600001
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.268 ± 0.023 2.243 2.309 1.00
pacquet@main 2.277 ± 0.039 2.201 2.321 1.00 ± 0.02
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.2683645768,
      "stddev": 0.02266870499011813,
      "median": 2.2603091037,
      "user": 3.90191412,
      "system": 3.03355992,
      "min": 2.2430828562,
      "max": 2.3093345022,
      "times": [
        2.3059813732,
        2.2526138132,
        2.2543389021999998,
        2.2602373451999997,
        2.2603808622,
        2.2616545562,
        2.2430828562,
        2.3093345022,
        2.2563438232,
        2.2796777342
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.2766221254000003,
      "stddev": 0.03947655940368133,
      "median": 2.2778195001999997,
      "user": 3.88905902,
      "system": 3.0286793199999997,
      "min": 2.2010253602,
      "max": 2.3213412231999997,
      "times": [
        2.2593740332,
        2.2491162652,
        2.2500010491999998,
        2.3213412231999997,
        2.2962649672,
        2.2553670662,
        2.3099299152,
        2.2010253602,
        2.3044177782,
        2.3193835962
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, hot cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.467 ± 0.021 1.443 1.498 1.00
pacquet@main 1.469 ± 0.021 1.447 1.515 1.00 ± 0.02
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.4669470359800003,
      "stddev": 0.021461817179532622,
      "median": 1.4614865913800001,
      "user": 1.7804941200000002,
      "system": 1.82304666,
      "min": 1.4427709823800001,
      "max": 1.4975596963800002,
      "times": [
        1.4913392193800001,
        1.4633370783800002,
        1.45560113338,
        1.44407101238,
        1.4596361043800001,
        1.4716981243800002,
        1.4427709823800001,
        1.4966490783800002,
        1.44680793038,
        1.4975596963800002
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.46905950698,
      "stddev": 0.02105900000281451,
      "median": 1.46420254938,
      "user": 1.7622540199999996,
      "system": 1.8219830600000002,
      "min": 1.4468804183800001,
      "max": 1.5149337413800001,
      "times": [
        1.47856599538,
        1.45668227938,
        1.4468804183800001,
        1.44757486638,
        1.5149337413800001,
        1.48332507638,
        1.47975809838,
        1.47172281938,
        1.4563313853800002,
        1.4548203893800002
      ]
    }
  ]
}

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/11977
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
🚷 view threshold
2,268.36 ms
(-9.50%)Baseline: 2,506.59 ms
3,007.91 ms
(75.41%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,466.95 ms
(-11.08%)Baseline: 1,649.72 ms
1,979.66 ms
(74.10%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
1,967.78 ms
(-3.25%)Baseline: 2,033.93 ms
2,440.72 ms
(80.62%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
650.09 ms
(-1.44%)Baseline: 659.62 ms
791.54 ms
(82.13%)
🐰 View full continuous benchmarking report in Bencher

- TokenStore's per-process secret now comes from getrandom (OS-backed
  CSPRNG) instead of time/pid/stack address. Tokens are derived from
  this secret + a per-issue nonce, so weak entropy was making mint
  outputs guessable to an attacker who could bound those inputs.
- Reorder derives on AuthConfig / HtpasswdConfig / TokensConfig /
  MaxUsers to satisfy perfectionist::derive-ordering (prefix-then-
  alphabetical: Debug, Default first, then the rest).
- Re-export auth::identify so the rustdoc link from the now-public
  UserStore::verify resolves; rustdoc::private-intra-doc-links no
  longer fails the workspace doc build.
- Drop the inaccurate "+inf" mention from MaxUsers' doc — serde-saphyr
  treats +inf as a float and can't deserialize it into i64, so the
  only way to get Unlimited is to omit max_users.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pnpr: persist users and tokens to disk

2 participants