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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
".": "0.3.0",
"desktop": "0.3.0"
".": "0.4.0-b",
"desktop": "0.4.0-b"
}
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# CHANGELOG

## [0.4.0-b](https://github.com/grayhatdevelopers/vidxp/compare/v0.3.0...v0.4.0-b) (2026-07-31)


### Features

* **api:** add explicit local network sharing ([#39](https://github.com/grayhatdevelopers/vidxp/issues/39)) ([37f3a84](https://github.com/grayhatdevelopers/vidxp/commit/37f3a84f03dcfc9022d723bac91d970352c3c1e3))


### Bug Fixes

* **models:** harden preparation downloads ([62cb7b1](https://github.com/grayhatdevelopers/vidxp/commit/62cb7b1b2dbdfbc968435bb76101b3199a88c1f8))
* **models:** harden preparation downloads ([69c5e51](https://github.com/grayhatdevelopers/vidxp/commit/69c5e51588c496699fe3a49b8c61a374f1058a98))

## [0.3.0](https://github.com/grayhatdevelopers/vidxp/compare/v0.2.0...v0.3.0) (2026-07-31)


Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \

ENV PATH="/opt/vidxp/bin:${PATH}" \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
PYTHONUNBUFFERED=1 \
VIDXP_HTTP_PORT=8000

USER vidxp
WORKDIR /var/lib/vidxp
Expand Down
39 changes: 32 additions & 7 deletions INSTALLATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,19 @@ vidxp doctor --modalities dialogue,actor
### 5. Start the selected surface

- CLI: `vidxp --help`
- Browser UI: `vidxp ui`
- Local HTTP API and remote MCP: `vidxp-api`
- Loopback browser UI: `vidxp ui`
- LAN-shared unauthenticated browser UI: `vidxp ui --share`
- Local HTTP API and MCP: `vidxp-api`
- LAN-shared authenticated HTTP API and MCP: `vidxp-api --share`
- Local stdio MCP: `vidxp-mcp`

The browser UI binds to loopback unless `--share` is present. In share mode,
VidXP gives Streamlit an explicit wildcard bind and Streamlit prints both the
Local and Network URLs. The UI has no authentication, so share it only on a
trusted network.
VidXP suppresses Streamlit's first-run email prompt and disables Streamlit
usage-statistics collection for this managed launch.

## First CLI index

```bash
Expand Down Expand Up @@ -263,15 +272,31 @@ Install `local-worker,server`, prepare models, then run:
vidxp-api
```

The default is reachable only from the same machine. To deliberately share it
on the machine's detected LAN address, run `vidxp-api --share`. Share mode:

- generates and then reuses an app-owned bearer token;
- binds Uvicorn to the detected LAN address;
- configures the HTTP and MCP Host-header policies for that address; and
- prints the exact health URL, Streamable HTTP MCP URL, and bearer token.

The managed token is stored as `api-share-token` in VidXP's platform-native
configuration directory. Share mode uses plain HTTP and is intended for a
trusted local network; use the supported reverse-proxy deployment when TLS is
required.

The unauthenticated local default is deliberately loopback-only:

| Endpoint | Purpose |
|---|---|
| `http://127.0.0.1:8000/docs` | Interactive OpenAPI |
| `http://127.0.0.1:8000/openapi.json` | Machine-readable contract |
| `http://127.0.0.1:8000/health` | Process liveness |
| `http://127.0.0.1:8000/ready` | Aggregate runtime readiness |
| `http://127.0.0.1:8000/mcp` | Streamable HTTP MCP |
| `http://127.0.0.1:32191/docs` | Interactive OpenAPI |
| `http://127.0.0.1:32191/openapi.json` | Machine-readable contract |
| `http://127.0.0.1:32191/health` | Process liveness |
| `http://127.0.0.1:32191/ready` | Aggregate runtime readiness |
| `http://127.0.0.1:32191/mcp` | Streamable HTTP MCP |

Native installs default to port `32191` to avoid the heavily reused development
port `8000`. Use `vidxp-api --port <port>` when a specific port is required.

Do not bind an unauthenticated API to a non-loopback address. Public
deployments require static bearer or OIDC authentication and should use the
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ uv tool install --python 3.14 --torch-backend cpu \
vidxp ui
```

`vidxp ui` binds to loopback by default. Use `vidxp ui --share` only when you
intend to expose the unauthenticated browser interface on the local network.
Streamlit prints its Local and Network URLs when it starts. VidXP disables
Streamlit's first-run email prompt and usage-statistics collection.

If the `vidxp` command is not found, run `uv tool update-shell` once and reopen
the terminal.

Expand Down Expand Up @@ -166,6 +171,12 @@ scenes, ask questions about a library, and create clips or actor overlays.
Local agents can connect over stdio; remote agents can connect to a
self-hosted VidXP server.

Agents can call `get_workspace` before acting to inspect registered media,
active-index coverage, model readiness, and the searchable, queryable,
inspectable, or renderable roles available for each video. Invalid capability
or media selections are rejected before a durable job is queued and include an
actionable next step.

- [Python, HTTP, and MCP installation](INSTALLATION_GUIDE.md)
- [Optional capability packages](INSTALLATION_GUIDE.md#optional-dependency-extras)
- [Coolify server setup](docs/deployment/coolify.md)
Expand Down Expand Up @@ -228,6 +239,13 @@ Contributions are welcome. Read the
## Credits

Built by Grayhat Developers PVT Ltd. and maintained by the community.
Originally researched by students:
- [Abdullah Mansoor](https://github.com/abdullahmansoor321)
- [Muhammad Haroon](https://github.com/haroon10725)
- [Sarah Jawaid](https://github.com/sarr266)
- [Talha Ahmed](https://github.com/talhaahmed1234)

Working with [Dr Shahab Tahzeeb](https://scholar.google.com/citations?user=cryeRB0AAAAJ&hl=en) ([NED University of Engineering and Technology](https://www.neduet.edu.pk/)) and [Saad Bazaz](https://scholar.google.com/citations?user=mrJo09oAAAAJ&hl=en) ([Grayhat](https://grayhat.studio).

Email: info@grayhat.studio

Expand Down
2 changes: 1 addition & 1 deletion desktop/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.0
0.4.0-b
4 changes: 2 additions & 2 deletions desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vidxp-desktop",
"private": true,
"version": "0.3.0",
"version": "0.4.0-b",
"type": "module",
"scripts": {
"tauri": "tauri",
Expand Down
4 changes: 2 additions & 2 deletions desktop/runtime-manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"schema_version": 1,
"desktop_version": "0.3.0",
"desktop_version": "0.4.0-b",
"package_name": "vidxp",
"package_version": "0.3.0",
"package_version": "0.4.0-b",
"dependency_index": "https://pypi.org/simple",
"dependency_constraints_sha256": "d3ed16906841952017f112903356bfe433a8515a37aa3f56e4af4dfa1f985835",
"python_version": "3.14.6",
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "vidxp-desktop"
version = "0.3.0" # x-release-please-version
version = "0.4.0-b" # x-release-please-version
description = "The VidXP desktop launcher and local runtime supervisor"
edition = "2024"
rust-version = "1.97"
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "VidXP",
"version": "0.3.0",
"version": "0.4.0-b",
"identifier": "dev.grayhat.vidxp",
"build": {
"frontendDist": "../web"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "vidxp"
version = "0.3.0"
version = "0.4.0-b"
authors = [
{ name = "Muhammad Haroon" },
{ name = "Talha Momin" },
Expand Down
4 changes: 1 addition & 3 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"bump-patch-for-minor-pre-major": false,
"draft": true,
"force-tag-creation": true,
"group-pull-request-title-pattern": "chore(release): prepare ${branch}",
"include-v-in-tag": true,
"prerelease": true,
"separate-pull-requests": false,
Expand All @@ -15,14 +14,13 @@
"type": "linked-versions",
"groupName": "VidXP",
"components": [
"vidxp",
"",
"desktop"
]
}
],
"packages": {
".": {
"component": "vidxp",
"exclude-paths": [
"desktop"
],
Expand Down
4 changes: 1 addition & 3 deletions release-please-config.stable.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,20 @@
"bump-patch-for-minor-pre-major": false,
"draft": true,
"force-tag-creation": true,
"group-pull-request-title-pattern": "chore(release): prepare ${branch}",
"include-v-in-tag": true,
"separate-pull-requests": false,
"plugins": [
{
"type": "linked-versions",
"groupName": "VidXP",
"components": [
"vidxp",
"",
"desktop"
]
}
],
"packages": {
".": {
"component": "vidxp",
"exclude-paths": [
"desktop"
],
Expand Down
8 changes: 8 additions & 0 deletions src/vidxp/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
def health() -> HealthResponse:
return HealthResponse()

@app.get(
"/favicon.ico",
include_in_schema=False,
status_code=204,
)
def favicon() -> Response:
return Response(status_code=204)

@app.get(
"/ready",
response_model=ReadinessResponse,
Expand Down
94 changes: 94 additions & 0 deletions src/vidxp/api_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@
from collections.abc import Sequence
from pathlib import Path

from vidxp.network_share import (
load_or_create_api_share_token,
primary_lan_address,
)


def _port(value: str) -> int:
try:
port = int(value)
except ValueError as exc:
raise argparse.ArgumentTypeError("port must be an integer") from exc
if not 1 <= port <= 65535:
raise argparse.ArgumentTypeError("port must be between 1 and 65535")
return port


def _arguments(values: Sequence[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
Expand All @@ -17,9 +32,65 @@ def _arguments(values: Sequence[str] | None = None) -> argparse.Namespace:
type=Path,
help="Store VidXP models and the default repository here.",
)
parser.add_argument(
"--port",
type=_port,
metavar="PORT",
help="Listen on this port instead of the VidXP default.",
)
parser.add_argument(
"--share",
action="store_true",
help=(
"Share the API and MCP on this machine's LAN address using an "
"app-managed bearer token."
),
)
return parser.parse_args(values)


def _shared_settings(settings, *, host: str, token: str):
from vidxp.settings import HttpAuthMode, VidXPSettings

if settings.http_auth_mode not in {HttpAuthMode.none, HttpAuthMode.static}:
raise ValueError(
"--share supports the app-managed static bearer mode, not OIDC."
)
active_token = (
settings.http_static_bearer_token.get_secret_value()
if settings.http_auth_mode == HttpAuthMode.static
and settings.http_static_bearer_token is not None
else token
)
payload = settings.model_dump(mode="python")
payload.update(
{
"http_bind_host": host,
"http_auth_mode": HttpAuthMode.static,
"http_static_bearer_token": active_token,
"http_trusted_hosts": tuple(
dict.fromkeys((*settings.http_trusted_hosts, host))
),
"mcp_allowed_hosts": tuple(
dict.fromkeys((*settings.mcp_allowed_hosts, f"{host}:*"))
),
}
)
return VidXPSettings.model_validate(payload), active_token


def _print_share_details(settings, token: str) -> None:
origin = f"http://{settings.http_bind_host}:{settings.http_port}"
print("VidXP network sharing is enabled.", flush=True)
print(f"Health: {origin}/health", flush=True)
print(f"MCP: {origin}/mcp", flush=True)
print(f"Bearer token: {token}", flush=True)
print(
"Keep this token private. LAN traffic uses HTTP and is not encrypted.",
flush=True,
)


def main(arguments: Sequence[str] | None = None) -> None:
options = _arguments(arguments)

Expand All @@ -33,7 +104,30 @@ def main(arguments: Sequence[str] | None = None) -> None:
if options.data_dir is not None
else VidXPSettings()
)
if options.port is not None:
payload = settings.model_dump(mode="python")
payload["http_port"] = options.port
settings = VidXPSettings.model_validate(payload)
share_token = None
if options.share:
if settings.http_auth_mode.value not in {"none", "static"}:
raise ValueError(
"--share supports the app-managed static bearer mode, not OIDC."
)
configured_token = (
settings.http_static_bearer_token.get_secret_value()
if settings.http_auth_mode.value == "static"
and settings.http_static_bearer_token is not None
else None
)
settings, share_token = _shared_settings(
settings,
host=primary_lan_address(),
token=configured_token or load_or_create_api_share_token(),
)
settings.validate_http_server()
if share_token is not None:
_print_share_details(settings, share_token)
uvicorn.run(
create_app(settings),
host=settings.http_bind_host,
Expand Down
2 changes: 1 addition & 1 deletion src/vidxp/api_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from vidxp.authentication import Authenticator


PUBLIC_HTTP_PATHS = frozenset({"/health", "/ready"})
PUBLIC_HTTP_PATHS = frozenset({"/favicon.ico", "/health", "/ready"})
UPLOAD_PATH = "/api/v1/media"


Expand Down
Loading