* fix(bot): only require VKE credentials when the TOS storage path needs them
Sweep findings: C-14. Gate AK/SK validation on an actual TOS deployment and drop the unused cluster ID.
(cherry picked from commit 8790ba509f)
* fix(server): apply configured temp_upload.default_mode to uploads
Sweep findings: B-11, D-01. Apply the documented configured upload mode when requests omit it.
(cherry picked from commit a0dc2498e8)
* fix(bot): make one-click Docker deployment generate a working config and port mapping
Sweep findings: C-12. Generate the active ov.conf and keep gateway and Docker ports aligned.
(cherry picked from commit c22af83ab6)
* fix(server): make --bot work and propagate bot flags to workers
Sweep findings: B-09, B-15. Honor the public Bot alias and replay resolved Bot settings in worker processes.
(cherry picked from commit 32a898ca14)
* fix(docker): derive entrypoint/health port from configured server port
Sweep findings: F-06. Keep server startup and every container health check on the same effective port.
(cherry picked from commit 000795c7e3)
---------
Co-authored-by: zhiheng.liu <zhiheng.liu@bytedance.com>
The runtime stage (Stage 3) was missing the git package, which is
required by GitAccessor at runtime for git clone/fetch/checkout
operations. Without it, any attempt to clone a repository would
fail with 'git: command not found'.
Co-authored-by: dingben.db@bytedance.com <dingben.db@bytedance.com@bytedance.com>
Previously /studio was only accessible in Docker builds because the
Dockerfile had a dedicated Node.js stage that built the SPA and copied
it into the Python package. pip/pipx/uv installs from source skipped
this step entirely, leaving openviking/web_studio/dist/ empty and
/studio unmounted at runtime.
Add a setuptools build_py hook (_build_web_studio) that automatically
runs `npm ci && npm run build` during package installation when Node.js
is available. The same function is reused by a new `make build-studio`
target and by the Dockerfile (which now gets Node.js via multi-stage
COPY from node:24-trixie-slim instead of a separate builder stage).
- setup.py: OpenVikingBuildPy overrides build_py to call _build_web_studio()
- Makefile: add build-studio target, wire it into `build` dependency
- Dockerfile: remove web-studio-builder stage, unify via build_py hook
- .gitignore: exclude openviking/web_studio/dist/ (build artifact)
The OpenViking docker image still launched the legacy `openviking/console`
standalone service on port 8020. Now that web-studio is bundled into the OV
server itself at /studio (see #2156), that process is redundant and the
port is just a confusing artefact.
This change retires the old console (python package + 8020 + console-frontend
favicons) but **keeps the in-compose Caddy as a stable single-ingress on
port 1934**, just simplified to one upstream now that there's no 8020. The
server-side BFF at `openviking/server/routers/console.py` (under
`/api/v1/console/*`) is also kept — web-studio uses the same endpoints.
**The OAuth authorize page (`openviking/server/oauth/router.py`) is
deliberately untouched in this PR** — the console-link button and Quick
authorize same-origin panel will be re-pointed at web-studio in a focused
follow-up.
BREAKING CHANGES:
- Port 8020 is gone from the docker image and docker-compose.yml; Caddy at
1934 now forwards everything to 1933 (web-studio lives at /studio there).
Anything bookmarked at `http://host:8020/...` must migrate to
`http://host:1933/studio/`.
- `python -m openviking.console.bootstrap` no longer exists; the python
package `openviking.console` has been removed.
Pip packaging:
- web-studio dist is now shipped inside the wheel under
`openviking/web_studio/dist/` (mirroring the old `openviking/console/static/`
layout). The dockerfile copies `--from=web-studio-builder /web-studio/dist`
into the source tree before `uv sync`, so the wheel produced by the
default docker build always carries the SPA. Building the wheel without
running `npm run build` first leaves the directory empty, which gracefully
degrades /studio to a 404 without breaking server startup.
- Favicon assets (`favicon.ico` / `favicon-32.png` / `apple-touch-icon.png`,
~11 KB total) are duplicated into `openviking/server/static/` and shipped
via package-data so `/favicon.*` and `/mcp/favicon.*` routes are always
registered, regardless of whether the web-studio dist is bundled.
- `pyproject.toml` and `setup.py` `package-data` drop `console/static/**`
and add `server/static/**` + `web_studio/dist/**`.
- New favicons (the 16/32/180 set in both `openviking/server/static/` and
`web-studio/public/`) are downscaled from the canonical
`web-studio/public/openviking-icon.png`, so the small-icon family matches
the SPA's high-res rel="icon" target — the studio tab icon now stays
consistent whether the browser uses the HTML link tag or falls back to
auto-fetching `/favicon.ico`.
Server:
- `openviking/server/app.py` now reads `/studio` from
`Path(__file__).parent.parent / 'web_studio' / 'dist'` by default;
`OPENVIKING_WEB_STUDIO_DIR` still wins for dev mode pointing at a
repo-local build. Favicon routes are unconditionally registered and
load from `openviking/server/static/`.
- `openviking/observability/usage_audit/projection.py` drops the legacy
`/console/*` skip prefix (the BFF prefix `/api/v1/console/*` remains).
Docker:
- `web-studio-builder` stage moved earlier (Stage 2) so its dist can flow
into `py-builder` before `uv sync` runs.
- Runtime stage no longer separately copies the dist or sets
`OPENVIKING_WEB_STUDIO_DIR`; the in-package path is the default.
- Entrypoint renamed `openviking-console-entrypoint.sh` -> `openviking-entrypoint.sh`
and stripped of the `python -m openviking.console.bootstrap` launch.
- `EXPOSE 1933 8020` -> `EXPOSE 1933`.
- `docker-compose.yml` drops the openviking service's 8020 port mapping;
the caddy service stays but no longer needs port 8020 exposed.
- `Caddyfile` simplified to a single `:1934 { reverse_proxy openviking:1933 }`
— the legacy `/console/*` route to :8020 is gone.
Docs:
- en/zh quickstart updated to drop the 8020 mapping and explain that the
API server now also serves `/studio`.
- Other guides (`12-public-access.md`, `11-oauth.md`, `05-observability.md`,
`04-setup-for-agent.md`, `03-deployment.md`) are intentionally left for a
focused follow-up PR alongside the OAuth quick-authorize reintroduction.
Tests:
- Deleted `tests/misc/test_console_{proxy,static_assets}.py` (covered the
removed console package). `tests/observability/test_console_router.py`
stays — it covers the BFF, which remains.
* feat(web-studio): bundle web-studio into docker, mount at /studio + favicon to public
- Dockerfile: add node:20 build stage for web-studio, `vite build --base=/studio/`
output to /app/web-studio/dist. New ENV OPENVIKING_WEB_STUDIO_DIR.
- server/app.py: serve /studio and /studio/{path:path} from dist; SPA deep-link
fallback to index.html. Favicon routes (/favicon.ico, /favicon.png,
/apple-touch-icon.png, /mcp/favicon.ico+png+touch) read from the same dist —
no more separate console/static dependency.
- web-studio/public: ship favicon.ico, favicon-32.png, apple-touch-icon.png
inside the SPA bundle so they end up in dist root.
Old console (8020) untouched in this branch.
* fix(web-studio): drop hardcoded 127.0.0.1:1933 fallback in ovClient default
ovClient was initialized with `baseUrl: ENV_BASE_URL || 'http://127.0.0.1:1933'`,
which short-circuited normalizeBaseUrl's window.location.origin fallback. As a
result, when web-studio was served by the OV server itself (e.g. bundled in the
OV docker image at /studio/), the SPA would try to call back to the user's local
127.0.0.1:1933 instead of the same origin it was loaded from — making the
bundled deployment unusable.
Removing the literal fallback lets normalizeBaseUrl pick window.location.origin
when ENV_BASE_URL is empty, which is the right default for every "served by OV"
shape (bundled image, port-forwarded docker, reverse-proxied custom domain).
Dev workflows running vite separately can opt in by setting VITE_OV_BASE_URL or
overriding the URL in the in-app Connection dialog (which persists to
localStorage).
---------
Co-authored-by: alice <alice@zouk-agent.local>
* perf(docker): add cargo + ccache cache mounts to py-builder stage
The two heavy RUN steps in py-builder (uv sync + maturin build) re-execute
on every Python source change because the upstream COPY layer for openviking/
invalidates the cache. Each rerun was ~510s + ~115s ≈ 10 min of wasted work
even though Rust/C++ source was unchanged.
Add BuildKit cache mounts so cargo and the C++ engine compilation can skip
work whose inputs are unchanged:
- Mount /cargo-target, cargo registry, and cargo git so cargo's incremental
build artifacts persist across layer reruns. Pin CARGO_TARGET_DIR so the
path stays stable when uv builds wheels in ephemeral isolated tempdirs.
- Install ccache and prepend /usr/lib/ccache to PATH so cmake (which calls
shutil.which("gcc")) resolves the ccache wrapper. ccache is path-agnostic,
so it benefits the cmake_build subdir even though setup.py recreates it
in a fresh tempdir each wheel build.
- Mount /root/.ccache so the ccache hash store persists across reruns.
Expected: hot rebuilds on Python-only changes drop step 15 from ~510s to
~60-120s (uv wheel packaging overhead remains; cargo + g++ skip on cache hit).
* perf(docker): drop redundant second maturin build step
The second RUN step in py-builder built ragfs-python a second time and
extracted its .so into the installed openviking package. This was
redundant: setup.py's build_ragfs_python_artifact() already runs maturin
during step 15 (uv sync --no-editable), and because build_meta passes
'bdist_wheel' through PEP 517, _should_require_ragfs_artifact() returns
True and the build fails closed if maturin can't produce ragfs_python.so.
The .so is then bundled into the wheel via package_data and installed
into /app/.venv on wheel install. The second step's only effect was to
overwrite the same file, costing ~115s per build.
Verified after the fact by inspecting the installed venv and importing
ragfs_python in the runtime container.
* feat(docker): serve pending-init details on every HTTP route
While the entrypoint waits for ov.conf to appear, run a tiny Python HTTP
server on the eventual server port (1933) that answers every request —
any path, any method — with the same 503 JSON describing the problem and
the fix. Operators and monitoring agents probing the container during
the bootstrap window now get an actionable error envelope instead of a
connection refused, while the Docker HEALTHCHECK still reports unhealthy
until the config is in place.
The pending server is shut down before the real openviking-server binds
the port, so there's no port-handover race.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: drop deployment guide mentions of pending health server
The pending HTTP server is a safety-net for the misconfigured case (no
ov.conf and no OPENVIKING_CONF_CONTENT) — surfacing it in the deployment
guide would frame it as a normal mode rather than a fallback. Document
the intent in the script's module docstring instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: drop pending health server unit tests
Same rationale as removing it from the user docs: the server is a
safety-net that runs only on the misconfigured path. The payload is a
small static JSON dict and the handler maps every method to one
function, so a regression here would be obvious from a one-shot manual
docker run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* style: ruff format + isort on pending_health_server.py
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(docker): collapse persistent state into /app/.openviking
The Docker image previously crashed on startup when ov.conf was missing,
making it impossible to docker exec in to fix the configuration. This
also forced two separate volume mounts (config + data), which is awkward
on managed platforms that only offer one persistent volume.
Changes:
- Set HOME=/app and put all persistent state under /app/.openviking, so
the in-container layout mirrors the host's ~/.openviking.
- docker-compose.yml mounts ~/.openviking -> /app/.openviking as a single
volume that captures ov.conf, ovcli.conf, and the workspace.
- Entrypoint bootstraps ov.conf from OPENVIKING_CONF_CONTENT when set;
otherwise prints a fix-it message and sleeps until the file exists, so
the container stays up for docker exec.
- openviking-server init honors OPENVIKING_CONFIG_FILE and derives the
workspace from its parent directory, so a single env var places init
output where the server will read it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(docker): describe single-mount layout and no-mount fallbacks
Switch all Docker examples to the new single-volume layout
(~/.openviking -> /app/.openviking) and document the two ways to
configure when bind mounts aren't available: pass the full ov.conf JSON
through OPENVIKING_CONF_CONTENT, or docker exec in and run
openviking-server init while the entrypoint waits for the file.
Updates docs/{en,zh}/getting-started/02-quickstart.md and
docs/{en,zh}/guides/03-deployment.md, including the macOS socat block.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: link
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the existing OPENVIKING_CONFIG_FILE=/app/ov.conf so the CLI
resolves /app/ovcli.conf instead of falling back to
~/.openviking/ovcli.conf when users exec into the container.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* docs: fix docker deployment
* reorg: remove third_party/agfs
* feat(s3fs): add disable_batch_delete option for OSS compatibility
Port of PR #1333 from Go version to Rust:
- Add disable_batch_delete config option to S3Client
- When enabled, use sequential single-object deletes instead of DeleteObjects
- This is for S3-compatible services like Alibaba Cloud OSS that require
Content-MD5 for DeleteObjects but AWS SDK v2 does not send it by default
- Add documentation and config example for OSS
* fix(s3fs): pass disable_batch_delete config from Python to Rust
Add disable_batch_delete to the s3_plugin_config dict in _generate_plugin_config
so that the Python config can properly control the Rust S3FS plugin's behavior.
* reorg: remove third_party/agfs
* reorg: remove third_party/agfs
* change some docs
* change some docs
---------
Co-authored-by: openviking <openviking@example.com>
The Dockerfile only includes the `bot` extra when installing dependencies,
which means the `google-genai` package is missing at runtime. This causes
the server to crash with `TypeError: 'NoneType' object is not callable`
when users configure the Gemini embedding provider.
Add `--extra gemini` to the `uv sync` commands so the Docker image ships
with Gemini support out of the box.
Fixes#1253
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* reorg: rewrite agfs with rust, and named with ragfs, keep License
* fix: grep level limit
* fix: grep root
* fix: import error
* fix: rust code optimazation
* fix: CI error
* fix: CI go mod cache
* fix: grep level limit
* fix: CI
---------
Co-authored-by: openviking <openviking@example.com>
Without --locked, uv sync will silently re-resolve dependencies if
pyproject.toml has drifted from the lockfile. This could pull in
unexpected package versions during Docker builds.
The --locked flag ensures the build fails fast if the lockfile is
stale, matching uv's recommended practice for production builds
(https://docs.astral.sh/uv/guides/integration/docker/).
The build_support/ module is required during the Docker build for
setup.py artifact builds but was missing from the COPY instructions,
causing build failures.
Co-Authored-By: Claude Opus 4.6