Run it inside your company's registry¶
Many companies let nothing onto a machine that did not come through their own
registry: JFrog Artifactory most often, or Sonatype Nexus, Azure Artifacts,
AWS CodeArtifact. Everything VectrixDB needs can come that way: the Python,
npm, Go and Rust packages, the container images, the models, and the
company's own wrapper of the command line and the clients. This page sets it
up in Artifactory, from an empty instance to a person running
acme vectors query, and then makes the company's wrapper the way in.
Throughout, the company is Acme, its Artifactory is acme.jfrog.io, and its
VectrixDB server is https://vectors.acme.com. Replace them with yours.
What comes through the registry¶
| What | Where it comes from in public | Artifactory repository | Who needs it |
|---|---|---|---|
vectrixdb and its Python dependencies |
pypi.org | PyPI remote | the server, the command line, Python developers |
vectrixdb for TypeScript |
registry.npmjs.org | npm remote | TypeScript developers |
github.com/knowusuboaky/VectrixDB/sdk/go/v2 |
proxy.golang.org | Go remote | Go developers |
vectrixdb for Rust |
crates.io | Cargo remote | Rust developers |
| The server and extraction images | ghcr.io | Docker remote | whoever runs the server |
The models download-models fetches |
GitHub releases | Generic remote | a server that needs the multilingual models, or an older collection's |
| The speech model the extraction image carries | huggingface.co | Hugging Face remote | building the extraction image in-house |
| The company's wrapper | built in-house | PyPI, npm, Go, Cargo local | everyone at the company |
The English models the server uses by default ship inside the Python package and the image, so a company that searches in English needs no model repository at all.
1. Create the repositories¶
In Artifactory, Administration, Repositories, create one remote
repository for each public source, one local repository for each kind of
package the company makes itself, and one virtual repository per language
that puts the two together. People and machines only ever use the virtual
one, so vectrixdb and acme-cli come from the same address.
| Repository | Type | Package type | URL to proxy |
|---|---|---|---|
pypi-remote |
remote | PyPI | https://files.pythonhosted.org, registry URL https://pypi.org |
pypi-local |
local | PyPI | |
pypi |
virtual | PyPI | pypi-local, then pypi-remote |
npm-remote |
remote | npm | https://registry.npmjs.org |
npm-local |
local | npm | |
npm |
virtual | npm | npm-local, then npm-remote |
go-remote |
remote | Go | https://proxy.golang.org |
go-acme |
remote | Go, VCS | the company's Git, for its own modules |
go |
virtual | Go | go-acme, then go-remote |
cargo-remote |
remote | Cargo | https://index.crates.io, with sparse index on |
cargo-local |
local | Cargo | |
cargo |
virtual | Cargo | cargo-local, then cargo-remote |
ghcr-remote |
remote | Docker | https://ghcr.io |
dockerhub-remote |
remote | Docker | https://registry-1.docker.io, for the Python base image when building in-house |
docker-local |
local | Docker | |
vectrixdb-models |
remote | Generic | https://github.com/knowusuboaky/VectrixDB/releases/download |
hf-remote |
remote | Hugging Face | https://huggingface.co |
Put the local repository first in each virtual one, so a package the company
publishes can never be shadowed by a public one of the same name. Where the
company's packages have a scope or prefix of their own (@acme/ on npm,
acme- on PyPI), add an exclude pattern for it on the remote repository
too: then nobody outside can publish a public acme-cli that a mistyped
install would pick up.
vectrixdb-models needs no credentials, because GitHub's releases are
public. Turn on Store artifacts locally, so each model is fetched from
GitHub once and served from Acme after that.
Scanning and curation¶
Attach the remote repositories to a JFrog Xray watch, so every package and image is scanned as it is first fetched, and, where JFrog Curation is licensed, a policy that blocks what the security team has not allowed. Our releases help:
- every container image is signed, with its SBOM and build provenance attached (Verify an image before it runs);
- every model zip is checked against checksums recorded in the package, so a mirror, or anything between it and GitHub, cannot change what is installed.
2. Give people and machines their tokens¶
Each person gets an Artifactory identity token, or signs in with
jf login, which keeps one. A CI job gets a short-lived token through
Artifactory's OpenID Connect integration with the CI platform (GitHub
Actions, GitLab, Azure DevOps, Jenkins), so no long-lived token sits in its
settings.
Read-only is all anyone but the release job needs. The token goes in
~/.netrc, the environment, or the operating system's keychain, as below;
never in a file that is committed.
3. Point each machine at it¶
Whoever manages machines puts these in place once, through device management,
the base image every developer starts from, or jf pip-config,
jf npm-config and friends. After that, plain pip install,
npm install, go get and cargo add go to Artifactory without anybody
typing its address.
pip.conf (~/.config/pip/pip.conf; %APPDATA%\pip\pip.ini on Windows),
with no credentials in it:
and the token in ~/.netrc (%USERPROFILE%\_netrc on Windows), readable
by its owner alone, which pip, Go and most other tools read:
uv and Poetry take the same address as an index.
.npmrc in the home folder:
Go signs in to the proxy with the same ~/.netrc line as pip.
GOPRIVATE keeps the company's own module names out of the public
checksum database. github.com/knowusuboaky/VectrixDB/sdk/go/v2 is public,
so its checksums are still checked; where sum.golang.org is out of
reach, Go asks for it through GOPROXY first.
~/.cargo/config.toml:
[registries.acme]
index = "sparse+https://acme.jfrog.io/artifactory/api/cargo/cargo/index/"
credential-provider = "cargo:token"
[source.crates-io]
replace-with = "acme"
[source.acme]
registry = "sparse+https://acme.jfrog.io/artifactory/api/cargo/cargo/index/"
and once: cargo login --registry acme with the token.
Check it from any machine:
pip download --no-deps "vectrixdb==2.2.0" -d /tmp/check # Python, through Artifactory
npm view vectrixdb version # npm
go list -m github.com/knowusuboaky/VectrixDB/sdk/go/v2@latest
cargo search vectrixdb --registry acme
4. Run the server from it¶
With the image¶
Pull the image through ghcr-remote, check its signature, and push it to
docker-local under the name the cluster uses, so what runs is the image
that was checked:
image=acme.jfrog.io/ghcr-remote/knowusuboaky/vectrixdb:2.2.0
docker pull "$image"
digest="$(docker inspect --format '{{index .RepoDigests 0}}' "$image" | cut -d@ -f2)"
# The digest is the same through the remote; the signature is checked at its source.
cosign verify "ghcr.io/knowusuboaky/vectrixdb@$digest" \
--certificate-identity-regexp '^https://github\.com/knowusuboaky/VectrixDB/\.github/workflows/containers\.yml@refs/(heads/main|tags/v.+)$' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
docker tag "$image" acme.jfrog.io/docker-local/vectrixdb:2.2.0
docker push acme.jfrog.io/docker-local/vectrixdb:2.2.0
cosign verify asks GitHub's registry and Sigstore's public transparency
log, so run it where those are reachable, such as the job that promotes
images into docker-local, and pin the digest it verified in the cluster
(Verify an image before it runs).
Built in-house¶
A company that builds every image itself builds ours from the source, with its own base image and every package and model through Artifactory. Two build secrets carry the addresses and credentials. They are mounted for the steps that need them and never written into a layer:
# pip.conf, for the build
[global]
index-url = https://build%40acme.com:<token>@acme.jfrog.io/artifactory/api/pypi/pypi/simple
# mirrors.env, for the build
VECTRIXDB_MODELS_URL=https://acme.jfrog.io/artifactory/vectrixdb-models
VECTRIXDB_MODELS_TOKEN=<token>
HF_ENDPOINT=https://acme.jfrog.io/artifactory/api/huggingfaceml/hf-remote
HF_TOKEN=<token>
docker build -f docker/Dockerfile \
--build-arg PYTHON_IMAGE=acme.jfrog.io/dockerhub-remote/library/python:3.12-slim \
--build-arg DEBIAN_UPDATES=0 \
--build-arg MODELS=all \
--secret id=pip,src=pip.conf \
--secret id=mirrors,src=mirrors.env \
-t acme.jfrog.io/docker-local/vectrixdb:2.2.0 .
docker build -f docker/Dockerfile --target extract \
--build-arg PYTHON_IMAGE=acme.jfrog.io/dockerhub-remote/library/python:3.12-slim \
--build-arg DEBIAN_UPDATES=0 \
--secret id=pip,src=pip.conf \
--secret id=mirrors,src=mirrors.env \
-t acme.jfrog.io/docker-local/vectrixdb-extract:2.2.0 .
DEBIAN_UPDATES=0 is for a build that cannot reach Debian's archive; the
company's own base image is then where its security fixes come from. Behind
a proxy that inspects TLS, add --secret id=ca,src=acme-ca.pem as well.
With pip¶
pip install "vectrixdb[api,signin,mcp]==2.2.0" # through pip.conf
export VECTRIXDB_MODELS_URL=https://acme.jfrog.io/artifactory/vectrixdb-models
export VECTRIXDB_MODELS_TOKEN="$JFROG_TOKEN"
vectrixdb download-models --type dense # only for the multilingual models
vectrixdb serve
VECTRIXDB_MODELS_URL replaces GitHub for every model download and takes the
same paths, <release tag>/<model>.zip. The token goes to that address
alone, never on to a redirect, and each model is checked against the
package's checksums before it is used. Set VECTRIXDB_OFFLINE=1 on a server
that should never download anything at all.
5. Ship the company's own tool¶
Build the wrapper as Ship your own client or command on it shows, with the company's address, gateway settings and sign-in presets in it, and publish it to the local repositories:
# Python: the command and the client
python -m build
twine upload --repository-url https://acme.jfrog.io/artifactory/api/pypi/pypi-local dist/*
# TypeScript
npm publish --registry https://acme.jfrog.io/artifactory/api/npm/npm-local/
# Rust
cargo publish --registry acme
# Go: a tag in the company's Git, which go-acme serves
git tag v1.0.0 && git push origin v1.0.0
A person at Acme then needs nothing but the package name:
pip install acme-cli
acme vectors login --device # their work account, once
acme vectors query "how long do refunds take?" --name handbook
Require the company's own tool¶
Making the wrapper available is the registry's job. Making it the way in is
the server's, because the registry cannot tell a person installing
acme-cli from one installing the vectrixdb it depends on.
- Register the wrapper as an app of its own with the identity provider:
a public client (no secret),
http://127.0.0.1as a redirect, the device code flow on, and permission for the server's API scope. Its client id is theVECTRIXDB_LOGIN_CLIENT_IDthe wrapper presets. -
Have the server take tokens from that app alone, and from the MCP clients the company allows:
export VECTRIXDB_OIDC_API_AUDIENCE="api://vectrixdb" export VECTRIXDB_OIDC_API_CLIENTS="<acme-cli client id> <MCP client id>"A token another app got for the same API is refused, and the access log records it as
app_not_allowed, with who it was. 3. Pin the scopes in the wrapper withVECTRIXDB_LOGIN_SCOPES, so it asks for this server's scope and nothing else, and refuses a server that asks for more. 4. Give keys to services, not people. An admin makes a key for each service or CI job, scoped to its collections and with an end date (vectrixdb keys add ... --days 90). People sign in.
What this does not stop: a client id is not a secret, since a public client cannot keep one, so somebody determined can copy the wrapper's into another tool. Step 2 keeps everyone on the wrapper by default and leaves a trace of anyone who goes around it; making it impossible is the job of the company's device management (which software may run) and its identity provider's conditional access (which devices may sign in), as it is for any company tool.
Upgrading¶
A new VectrixDB release reaches Acme when somebody first asks for it: the
remote repositories fetch it, Xray scans it, and Curation lets it through or
not. The wrapper pins the major version (vectrixdb[client]>=2.2,<3), so a
2.x release reaches people with the wrapper's next build, and a 3.0 waits for
the wrapper to move. Upgrade the server first: a client older than the
server keeps working, since a 2.x server only adds.
Checklist¶
| Step | Done when |
|---|---|
| Repositories | pip download vectrixdb, npm view vectrixdb, go list -m ..., cargo search vectrixdb all answer through Artifactory |
| Images | the image is in docker-local, its signature verified and its digest pinned |
| Models | vectrixdb download-models --type dense with VECTRIXDB_MODELS_URL set fetches through vectrixdb-models |
| Wrapper | pip install acme-cli on a fresh machine, then acme vectors login --device and a query, work |
| Required | a token from another app gets 401, and the access log says app_not_allowed |