The first-party Knowledge built-in app UI, served by cmd/server.
It follows Directory's Vite / React / TanStack Query and Router package layout,
with Japanese and English copy from @appthrust/platform-dashboard-i18n.
The appthrust-language cookie preserves the language choice (Japanese by default).
/: search playground, collection filters, hybrid / lexical / vector mode, topK 1–50 (default 8), ranked document/chunk text and score. If/configsaysllm.configured, the LLM optionsrerankandexpand(official terms and rephrasings, shown as chips from the response'sexpanded) are offered and Ask streams citations, tokens and completion from/answer./collections: list, create and delete collections; comma/newline-separated visibility group IDs. Empty groups mean project-wide visibility./documents: per-collection documents; synchronously ingest up to 1 MiB of Markdown, inspect chunk counts and delete documents./sources: add a Notion source (integration token, root page IDs and database IDs) or an esa source (access token, team subdomain, optional esa search query, optional WIP inclusion). Sync now enqueues a background job (202 {job}); while a source's latest job (sync) is queued or running,GET /sourcesis polled every 2 s and the row shows documents/chunks so far (against the total when the source reports one); a failed job shows its error above the last sync. The last sync block shows the counts, completion time and individual errors of the last finished job. Tokens are write-only and never read back; when/configreportsnotion.platformManaged/esa.platformManaged, the token input is replaced by a link to the Project's built-in app settings and no token is sent. Notion and esa permissions are not copied: the collection visibility applies to imported documents.
The header reads project identity and the public URL from /config; the footer
reads /releasez (release version and source revision; full identity on hover).
It also shows the embedding source and dimensions from /config: embedding
is the built-in Embedding app (in-cluster), openai is OpenAI's hosted endpoint,
custom is another explicit endpoint, and hashing is local feature hashing.
Search summaries use the same localized source labels. The API's
embedding.provider remains the wire protocol; embedding.source is the
display identity, falling back to provider for older servers. When
llm.configured is true, the footer includes llm.provider (gateway is
labelled AI gateway); unconfigured LLMs omit the provider in /config.
While /config reports embedding.backfill.state queued or running (chunks
from a previous embedding model being re-embedded), every page shows a banner
with the remaining document count and /config is polled every 5 s.
Missing release identity does not prevent using the application.
Use Go 1.26 or newer and Bun 1.4.1. From web/, install the published
GitHub Packages dependencies with NODE_AUTH_TOKEN="$(gh auth token)" bun install --frozen-lockfile.
Then start the mock and development server:
bun run mock
# Another terminal:
bun run devOpen http://127.0.0.1:5208. The mock listens on 127.0.0.1:5206; override with
KNOWLEDGE_PORT / KNOWLEDGE_MOCK_PORT. Vite proxies /api, /config and
/releasez and /launcher to the mock. For a real server set KNOWLEDGE_API_UPSTREAM and either
KNOWLEDGE_DEV_BEARER or KNOWLEDGE_DEV_COOKIE_FILE (the latter contains the
Cookie header and is re-read per request). These credentials stay in the dev
proxy; the SPA sends only same-origin session cookies. Never put credentials in
VITE_* environment variables.
The mock implements the C4 routes under /api/knowledge/v1, with {data:[]}
lists and direct single-resource responses. It starts with a handbook
collection and one document. State resets on process restart. Markdown writes
upsert by collection/externalId, split into 1000-character chunks, and use
256-dimensional deterministic feature hashing plus lexical scores and RRF.
This is a UI fixture, not a substitute for PostgreSQL/pgvector proof. Sync
enqueues a job on one FIFO worker: it stays queued for 0.5 s, then runs for
2.5 s, upserting documents one by one (GET /api/knowledge/v1/jobs/{id} reads
it). Notion sync creates clearly labelled local documents, bounded to 100 IDs,
without a known total; IDs beginning error: exercise partial errors and an ID
beginning fail: fails the job. esa sync creates two labelled posts
(esa:<team>:<number>) against a total of 2; a query containing error: adds a
partial error and one containing fail: fails the job. No Notion, esa or model
API is called. Search with expand returns expanded: ["年次有給休暇", "休暇申請"]. KNOWLEDGE_MOCK_NOTION_PLATFORM_MANAGED=1 /
KNOWLEDGE_MOCK_ESA_PLATFORM_MANAGED=1 report the respective token as
platform-managed and reject supplied tokens with 400
knowledge_notion_token_platform_managed / knowledge_esa_token_platform_managed.
GET /api/knowledge/v1/providers/esa reports {platformManaged:false} by default
and, in esa platform-managed mode, a connection that reads the docs and
engineering teams (the Sources form then offers them as a team select).
KNOWLEDGE_MOCK_ESA_CONNECTION_ERROR=1 implies esa platform management and
reports a rejected token (connected:false, error: "esa returned HTTP 401").
Mock answers are labelled as mock and streamed with citations first;
KNOWLEDGE_MOCK_LLM=0 disables Ask, rerank and expand and returns 503
knowledge_llm_unavailable. KNOWLEDGE_MOCK_BACKFILL=1 makes /config report a
re-embed backfill of 48 documents from its first read: queued for 1 s, then 8
fewer every 2 s until idle.
| Flag | Effect |
|---|---|
KNOWLEDGE_MOCK_PORT |
Mock listen port (default 5206) |
KNOWLEDGE_MOCK_LLM=0 |
No LLM: no Ask, rerank or expand (503) |
KNOWLEDGE_MOCK_NOTION_PLATFORM_MANAGED=1 |
Notion token managed by the Project |
KNOWLEDGE_MOCK_ESA_PLATFORM_MANAGED=1 |
esa token managed by the Project; docs/engineering teams |
KNOWLEDGE_MOCK_ESA_CONNECTION_ERROR=1 |
esa platform-managed with a rejected token |
KNOWLEDGE_MOCK_BACKFILL=1 |
Re-embed backfill countdown on /config |
From web/:
bun run typecheck
bun run test
bun run build:imagebuild:image writes web/dist/; build first typechecks.
Tests cover search, job and config DTO parsing, fragmented UTF-8/CRLF SSE,
error and truncated streams, mock CRUD/upserts/token redaction, the sync job
lifecycle (on a test clock), expand and backfill, and each route rendered with
mock API data in both locales. Browser screenshots belong in ignored .tmp/.
The client reports server errors without silently converting them to empty
results. Cancelling Ask or leaving its screen aborts the answer request; a stream
without done is an error, not a successful partial answer.
The server and all of its internal packages live in cmd/server/. It requires
github.com/appthrust/appserver-go v0.2.0 without a replacement or platform
checkout. PostgreSQL with pgvector stores Knowledge data; configure
KNOWLEDGE_DATABASE_URL (or DATABASE_URL) and the PLATFORM_* identity and API
settings before running a real integration. KNOWLEDGE_EMBEDDING_URL and
KNOWLEDGE_LLM_URL are supplied by the catalog's automatic embedding and
ai-gateway dependencies. The access-key audience remains knowledge.
From the repository root:
go vet ./...
go build ./... && go test ./...
bun test hack/
go run ./cmd/server --static-dir web/distPostgreSQL integration tests are opt-in: set
BUILTIN_APPSERVER_TEST_DATABASE_URL to a disposable pgvector-enabled database.
Tests create isolated schemas, and the role/extension privilege test requires a
superuser test DSN. Without a test database, the integration cases skip.
The Docker image builds the Vite SPA and Go binary separately, then serves the
SPA from /srv/knowledge using /knowledge-server as user 65532 on port
8080. No JavaScript runtime is included in the final distroless image.
NODE_AUTH_TOKEN="$(gh auth token)" docker build --platform linux/amd64 \
--secret id=NODE_AUTH_TOKEN,env=NODE_AUTH_TOKEN -t knowledge-local .Once the bootstrap prerequisites below are complete, create and push a new
SemVer tag, starting with v0.2.0 (the existing catalog floor owns 0.1.0):
git tag v0.2.0
git push origin v0.2.0.github/workflows/publish.yml builds Linux amd64 and arm64, pushes the index to
ghcr.io/appthrust/knowledge:0.2.0, resolves its immutable digest, and signs that
digest with Sigstore keyless signing. Both the source repository and the published image must remain public. GITHUB_TOKEN needs packages:write and OIDC needs
id-token:write. GitHub Packages must grant this repository read access to
@appthrust/app-launcher and @appthrust/platform-dashboard-i18n; npm credentials
enter the Docker build only as a BuildKit NODE_AUTH_TOKEN secret.
Configure repository secret CLOUD_CATALOG_ADMIN_TOKEN and, if needed, variable
CLOUD_API_URL (default https://api.appthrust.com). Grant this repository access
to organization secrets APPTHRUST_GITHUB_ACTIONS_APP_ID and
APPTHRUST_GITHUB_ACTIONS_APP_PRIVATE_KEY. The GitHub App must be installed for
appthrust/cloud with Contents write permission to send repository dispatches.
Cloud's API needs no GitHub credential.
Publication has two phases:
bun hack/publish-entry.ts submit --version 0.2.0 --digest sha256:<image-index-digest>
# If submit reports state=snapshot, dispatch with an installation token for Cloud:
gh api -X POST repos/appthrust/cloud/dispatches -f event_type=catalog-publish -f 'client_payload[snapshot_id]=<snapshot-id>'
bun hack/publish-entry.ts await --version 0.2.0 --digest sha256:<image-index-digest>submit reads the existing Cloud entry, merges its version history with
catalog/entry.yaml, and PUTs the complete CatalogEntry to
/api/v1/catalog/entries/knowledge. It then POSTs /api/v1/catalog/publish,
accepting either HTTP 202 {id, state: "queued"} (Cloud dispatched publication)
or HTTP 200 {id, state: "snapshot"} (the immutable snapshot needs dispatch).
It writes snapshot_id and state to $GITHUB_OUTPUT in Actions and exits
without polling. For snapshot only, the workflow mints a short-lived GitHub App
installation token scoped to appthrust/cloud and dispatches
the catalog-publish repository event with that snapshot id. For queued, it skips dispatch.
Cloud's workflow requires its own CLOUD_CATALOG_API_URL repository variable and
CLOUD_CATALOG_ADMIN_TOKEN secret to fetch the snapshot.
await requires no Cloud admin token. Dispatch is not success: every 15 seconds
for at most 10 minutes it fetches layer 0 (index.json) of
ghcr.io/appthrust/catalog:latest through ORAS, checks its digest, and requires
the exact Knowledge version and image digest. Before succeeding it verifies
that immutable catalog's signature against issuer
https://token.actions.githubusercontent.com and identity
https://github.com/appthrust/cloud/.github/workflows/catalog-publish.yml@refs/heads/main.
Timeout, API failure, dispatch failure, or an unverified artifact fails the release.
ORAS 1.3.0 and Cosign 3.1.3 are installed by the workflow; manual publication
needs them too.
catalog/entry.yamlis the complete ApplicationStackDefinition. Its versions section is the publisher's append point, not an image tag substitution.- The seed
0.1.0retains its originalghcr.io/appthrust/platform/knowledge@sha256:...image. All subsequent versions useghcr.io/appthrust/knowledge@sha256:<index-digest>,stream: <major.minor>,served: true, anddefault: true. Earlier versions stay served with their defaults cleared. The definition and top-level catalog versions agree. - A version's images are immutable. Repeating the same version/digest is idempotent; attempting to replace its digest fails. The release workflow serializes Knowledge writers to protect its read/merge/PUT sequence.
- Changing from the seed's repository requires an explicit installation version
update with
replaceRepository: true. Afterwards thefollowpolicy follows its stream;pinnedrequires an explicit version update. /releasezreports catalog-renderedKNOWLEDGE_RELEASE_VERSIONandKNOWLEDGE_ARTIFACT_DIGEST; no platform release closure is involved.
@appthrust/app-launcher and @appthrust/platform-dashboard-i18n are public
GitHub Packages owned by appthrust/platform. GitHub Packages still requires
a token for downloads: CI and the publisher use this repository's
GITHUB_TOKEN as NODE_AUTH_TOKEN (web/.npmrc); GitHub App installation
tokens are rejected by the npm registry (403). Locally:
NODE_AUTH_TOKEN="$(gh auth token)" bun install in web/.
web/bun.lock is registry-resolved and committed; CI and Docker use
--frozen-lockfile.
The committed compatibility floor is the complete spec read from
ApplicationStackDefinition/knowledge on appthrust-ink-pmc001 on 2026-11-06.
It preserves the served 0.1.0 version and image, both automatic dependencies,
and the access-key audience and scopes verbatim. The first independent release
is 0.2.0 in stream 0.2; no placeholder image is committed for it.
Architecture and migration authority:
first-party-app-repositories.md.
Imported from platform commit 27347914ee15817f27a42055160b42506edc73e6,
cmd/knowledge-server/ and dashboard/packages/knowledge/; this README retains
the SPA's operating guide. The publication workflow follows appthrust/directory.