* fix(ov): redact gateway secrets in config show and create root key with 0600
Sweep findings: E-02, E-03. Prevent credential disclosure in output and at key creation.
* test(ov): drop trivial init-key permission test
The 0600 fix is a one-line OpenOptions::mode; a dedicated tokio+tempfile
test module for a single mode assertion is not worth its weight. The
redaction test in store.rs (a real multi-case security behavior) stays.
When the server crawls a URL that returns 401/403, it wraps the failure
in a 5xx envelope. The CLI matched on auth-flavored message text alone
and rendered "OpenViking rejected the API key", wrongly pointing users
at their local config.
Gate the API-key error report on status: 5xx responses are never treated
as a client auth failure even when the message mentions authentication or
forbidden. Also split the 401 vs 403 fetch messages so 403 reads as an
access-denied / anti-bot block rather than a credential problem.
* feat(storage): optimize glob func
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* fix(localfs): offload blocking fs operations to spawn_blocking
* feat(glob): cap glob api default node_limit at 256
* feat(sdk): add node_limit options for glob in python and go SDKs
* feat(auth): support seeded API key generation
Allow admin key issuance flows to accept an optional seed so clients can derive predictable user API keys when needed, while preserving random generation by default.
* fix(go-sdk): preserve explicit empty seed payloads
Use pointer seed options so callers can distinguish omitted seeds from explicit empty seeds, matching the Admin API behavior.
Add WebFeedAccessor (priority 60) that turns a single sitemap /
sitemapindex / RSS / Atom URL into ONE resource tree: it mirrors every
listed page into a temp directory and reuses the existing DirectoryParser
pipeline (the same "fetch-many -> dir -> tree" contract as GitAccessor).
A watch on the feed URL keeps the whole site refreshed (new pages added,
removed pages dropped on each rebuild).
- New openviking/parse/accessors/web_feed_accessor.py: WebFeedAccessor +
sitemap/feed extractors (nested sitemapindex recursion with depth cap,
RSS 2.0 / Atom via feedparser), bounded concurrent polite mirroring,
robots.txt, same-host / include / exclude / max_pages limits.
- args={"site": true} forces whole-site ingestion from a bare domain or
page by auto-discovering the sitemap/RSS (robots.txt, HTML
<link rel=alternate>, conventional paths); {"site": false} opts a
feed-looking URL back out to HTTPAccessor.
- Thread accessor-selection kwargs through can_handle; the registry
tolerates accessors whose can_handle lacks **kwargs (back-compatible).
- Single-page adds get a non-blocking "this site exposes a sitemap/RSS"
suggestion appended to the MCP add_resource response, gated to the
site root only; never auto-crawls.
- New WebFeedConfig (parsers.webfeed): max_pages, concurrency, politeness
delay, same_host_only, respect_robots, max_depth, suggest_feed.
- Dependencies: feedparser (robust RSS/Atom), defusedxml (XXE-safe XML).
- Docs: zh/en resources API, MCP/CLI/SDK help, ov.conf.example.
- Tests: 52 unit tests (fake httpx, no network).
* chore: clear unused files
* fix(tests): fix unit test
* refactor(auth): introduce plugin-based authentication architecture
Replace the monolithic `openviking/server/auth.py` with an extensible
plugin-based auth system. This refactor extracts the three built-in modes
(`dev`, `api_key`, `trusted`) into separate `AuthPlugin` implementations,
adds a registry for third-party plugins, and preserves all existing behavior
while enabling custom authentication backends (e.g. LDAP, OIDC, mTLS).
Key changes:
- **New public API**: `AuthPlugin` (ABC) and `register_auth_plugin` decorator.
- **New registry**: `AuthPluginRegistry` supports runtime registration.
- **Built-in plugins**: `DevAuthPlugin`, `ApiKeyAuthPlugin`, `TrustedAuthPlugin`.
- **Config change**: `auth_mode` widened from `Literal` to `str` for custom modes.
- **Validation delegated**: `validate_server_config()` now delegates to the active
plugin's `validate_config()`, preserving existing validation semantics.
- **Router compatibility**: All existing `require_*` decorators and `resolve_identity`
/ `get_request_context` dependencies remain unchanged. Routers import the same
symbols from `openviking.server.auth`.
- **Tests**: `conftest.py` manually wires the DevAuthPlugin in ASGI tests (lifespan
not triggered). `test_auth.py` expanded with plugin registration and validation tests.
- **Docs**: `04-authentication.md` (en/zh) updated with plugin registration examples.
Co-Authored-By: claude-sonnet-4-6 <noreply@anthropic.com>
* fix(tests): fix trusted mode test
* fix(tests): fix unit test
* fix(cli): remove unexisted transaction observer
* docs: update skills definition
* docs: update skills definition
* docs: update skills definition
* docs: update skills definition
* fix(skills): now we allow viking://agent/skills again, and optimize CLI for skills
* docs(skills): use -p instead of --parent in agent skills examples
Align the `ov skills add` examples in the context-types and viking-uri
docs with the short flag `-p` introduced for `ov skills list/find/show`,
so all four user-facing examples consistently demonstrate the short form
when targeting `viking://agent/skills`.
Co-Authored-By: claude-sonnet-4-6 <noreply@anthropic.com>
* fix(tests): error check for api key
* fix(tests): unit test wait until resource not busy
* fix(tests): unit test wait until resource not busy
* fix(sdk): args form in skills find
* fix(skills): pass target uri in request body
---------
Co-authored-by: claude-sonnet-4-6 <noreply@anthropic.com>
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
The CLI unconditionally attaches optional fields to the find/search/add_resource
request bodies — `args` as an empty `{}` and `tags`/`context_type` as `null` —
even when the user never set them. OpenViking kernels use
`model_config = ConfigDict(extra="forbid")` on these routes, so any instance that
predates a field rejects the whole request with
`body.<field>: Extra inputs are not permitted`. This breaks whenever the CLI is
newer than the target instance (and, symmetrically, when a field is later
renamed/removed): e.g. `ov add-resource` fails on `body.args` against a pre-#2549
instance, and `ov find` fails on `body.tags` against a strict pre-#2706 instance.
- Add `compact_request_body()` and apply it in find/search/add_resource: drop
null-valued keys and an empty `args` object before sending. Scoped to these
read/create routes only, where a missing optional field and an explicit null
are equivalent — not applied globally, since null may mean "clear" on a future
update/PATCH route. This mirrors the existing `create_parent` backward-compat
convention.
- When a field is explicitly set but unsupported, translate the raw pydantic
`Extra inputs are not permitted` error into a version-mismatch hint and suggest
`ov health`, instead of surfacing the opaque API error.
Tests: unit tests for `compact_request_body` and `extra_forbidden_field`.
validation_error_copy() took _error: &Error but completely ignored
it, always returning the same generic message regardless of the actual
failure. Users saw 'Validation failed. Check the server URL and API
key if required.' for every kind of error — network unreachable,
unhealthy server, rejected API key, or HTTP errors.
Now inspects the error to provide actionable messages:
- Network errors → 'Cannot reach the server. Check the URL.'
- Unhealthy server → 'Server reported unhealthy status.'
- Auth rejected (401/403) → 'API key was rejected.'
- HTTP errors → 'Server returned HTTP {status}.'
- Config errors → passes through the original message
- Other → original generic fallback
Both English (validation_error_copy) and Chinese
(validation_error_copy_zh) paths updated.
* chore: clear unused files
* fix(tests): fix unit test
* refactor(auth): introduce plugin-based authentication architecture
Replace the monolithic `openviking/server/auth.py` with an extensible
plugin-based auth system. This refactor extracts the three built-in modes
(`dev`, `api_key`, `trusted`) into separate `AuthPlugin` implementations,
adds a registry for third-party plugins, and preserves all existing behavior
while enabling custom authentication backends (e.g. LDAP, OIDC, mTLS).
Key changes:
- **New public API**: `AuthPlugin` (ABC) and `register_auth_plugin` decorator.
- **New registry**: `AuthPluginRegistry` supports runtime registration.
- **Built-in plugins**: `DevAuthPlugin`, `ApiKeyAuthPlugin`, `TrustedAuthPlugin`.
- **Config change**: `auth_mode` widened from `Literal` to `str` for custom modes.
- **Validation delegated**: `validate_server_config()` now delegates to the active
plugin's `validate_config()`, preserving existing validation semantics.
- **Router compatibility**: All existing `require_*` decorators and `resolve_identity`
/ `get_request_context` dependencies remain unchanged. Routers import the same
symbols from `openviking.server.auth`.
- **Tests**: `conftest.py` manually wires the DevAuthPlugin in ASGI tests (lifespan
not triggered). `test_auth.py` expanded with plugin registration and validation tests.
- **Docs**: `04-authentication.md` (en/zh) updated with plugin registration examples.
Co-Authored-By: claude-sonnet-4-6 <noreply@anthropic.com>
* fix(tests): fix trusted mode test
* fix(tests): fix unit test
* fix(cli): remove unexisted transaction observer
---------
Co-authored-by: claude-sonnet-4-6 <noreply@anthropic.com>
Fixes unbounded queue.db growth for newly created QueueFS SQLite
databases by enabling SQLite auto_vacuum=FULL at DB initialization.
- Detect brand-new databases (no tables in sqlite_master)
- Set PRAGMA auto_vacuum=FULL and run VACUUM before schema creation
- Preserve existing deployed databases unchanged (no risky rewrites)
- Add regression tests for both new-db and legacy-db behavior
Fixes#2707
* fix(cli): filter non-Press key events to prevent double-registration in interactive prompts
On Windows with ENABLE_VIRTUAL_TERMINAL_INPUT enabled (crossterm 0.28.1),
the console can generate both Press and Release key events for a single
physical key press. The TUI runner (tui/mod.rs) already filters for
KeyEventKind::Press, but the config wizard, config switch selector,
and skill multi-select did not, causing double-processing of every
key press.
Add key.kind == KeyEventKind::Press guard to all three interactive
event-reading loops:
- config_wizard/wizard.rs: prompt_select and prompt_text
- handlers.rs: config switch prompt_select
- commands/skills.rs: skill multi-select prompt
Also add explicit Event::Key(_) => {} arms to discard non-Press
key events cleanly.
* perf(cli): render only on state change, not on every event loop iteration
Previously ui.render() was called at the top of every event loop
iteration, causing a full clear-and-redraw for every ignored event
(Release, Resize, and other non-Press events). On Windows with
ENABLE_VIRTUAL_TERMINAL_INPUT enabled, crossterm can emit multiple
events per physical key press, leading to excessive flickering.
Restructure all three interactive event loops to:
- Render once initially before the loop
- Re-render only when selection/state actually changes (Up/Down/Space)
- Re-render on terminal resize
- Skip render entirely for Release, unhandled Press, and other events
* add CachedFileSystem + CacheProvider trait and Mooncake/Yuanrong Provide
add CachedFileSystem + CacheProvider trait and Mooncake/Yuanrong Provide
* Handle poisoned known_keys mutexes in cache providers
* Add configurable cache support to ragfs python
* Add Redis cache provider support
* Prune native cache providers from default build
* Add RAGFS cache guides in Chinese and English
* delete .cargo/config.toml
* Fix stale cache invalidation across shared wrappers
* Document Yuanrong native concurrency limits
* add CachedFileSystem + CacheProvider trait and Mooncake/Yuanrong Provide
add CachedFileSystem + CacheProvider trait and Mooncake/Yuanrong Provide
* Handle poisoned known_keys mutexes in cache providers
* Add configurable cache support to ragfs python
* Add Redis cache provider support
* Prune native cache providers from default build
* Add RAGFS cache guides in Chinese and English
* delete .cargo/config.toml
* Fix stale cache invalidation across shared wrappers
* Document Yuanrong native concurrency limits
* Limit directory cache entries and add regression test
* docs: add TOS s3fs backend test design
* Add runtime cache config override
* add build guides for mooncake and yuanrong
* fix _FakeConfig test with cache
* fix(ragfs): cache encrypted data below the encryption layer
- pass runtime cache config to the Rust binding
- cache ciphertext instead of decrypted content
- disable cache for encrypted multi-write mounts
- update tests and provider build documentation
* fix(ragfs): invalidate cache after same-mount raw copy
Ensure copy_within_mount invalidates the cached destination file and
parent directory after the raw backend fast path writes data directly.
This prevents stale reads and stale directory metadata when cache is
enabled and Python cp() uses the same-mount copy optimization.
Add a regression test covering overwrite copy followed by read/list.
* fix(ragfs): preserve multi-write discovery through cache layer
Allow MountableFS::as_multiwrite() to unwrap CachedFileSystem when
discovering the underlying MultiWriteWrappedFS. This preserves
multi-write admin paths and same-mount copy behavior for cache-enabled,
unencrypted multi-write mounts.
Add a regression test covering sync status, sync retry, same-mount copy,
and unmount behavior for cached unencrypted multi-write mounts.
* feat(ragfs): add cache-aware tree traversal mode
- add configurable tree traversal mode to cache policy
- keep default tree behavior delegated to backend
- allow cached traversal to reuse read_dir directory cache
- bypass cached traversal for multi-write backends
- add regression coverage for tree cache behavior and fallbacks
* docs: design cache-aware grep traversal
* feat(ragfs): add cache-aware grep traversal
Introduce a shared cache traversal mode for recursive APIs and use it to
optionally run grep through CachedFileSystem.
- add CacheTraversalMode with backend and cached_traversal modes
- keep CacheTreeMode as a compatibility alias
- route tree and grep through cached traversal only when explicitly enabled
- reuse cached read_dir entries and full-file reads during grep traversal
- keep multi-write traversal on the backend path
- expose storage.agfs.cache.traversal_mode in Python config
- raise max cached directory entries threshold to 4096
- add regression tests for grep cache traversal and traversal config
* Optimize cached grep generation validation
* Parallelize cached grep file scanning
---------
Co-authored-by: fang <fang@fangMacBook-Air.local>
* feat(cli): improve config TUI
Add a Switch Config entry to the interactive ov config menu and route it through the existing config switch flow.
Make the config status header responsive with full and compact modes, keeping the original wordmark/boat art only when the terminal has enough room.
Fix wrapped-row clearing for config, switch, skill removal, help, and error surfaces so terminal resizes do not leave stale borders or duplicated content.
* fix(cli): address config TUI review feedback