Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
234 commits
Select commit Hold shift + click to select a range
8de429c
fix(semantic): preserve graph fact source provenance
seonghobae Aug 25, 2026
476d761
docs(gaps): track graph fact prompt provenance
seonghobae Aug 25, 2026
813ca0a
fix(chat): retain isolated post graph evidence
seonghobae Aug 25, 2026
c519353
docs(gaps): correct hourly caller delivery evidence
seonghobae Aug 25, 2026
f98fdeb
docs(gaps): keep acceptance loop numbering contiguous
seonghobae Aug 25, 2026
6e781f0
fix(chat): keep graph fact prompt cap global
seonghobae Aug 25, 2026
7381c9e
docs(gaps): finish contiguous loop numbering
seonghobae Aug 25, 2026
a60c5b4
docs(gaps): track central gateway delivery
seonghobae Aug 25, 2026
fb6aa44
fix(semantic): hide unauthorized graph post endpoints
seonghobae Aug 25, 2026
6b99489
feat(semantic): nominate Global Ask evidence candidates (#637)
seonghobae Aug 25, 2026
bb68032
docs(gaps): record semantic candidate exact head
seonghobae Aug 25, 2026
187a483
test: keep synthetic snapshot digests unique
seonghobae Aug 25, 2026
3851c7c
fix(security): keep landing query static
seonghobae Aug 25, 2026
bfeaecd
fix: honor disabled RankWeave fusion
seonghobae Aug 25, 2026
52d2368
feat(ask): verify public semantic claims (#641)
seonghobae Aug 25, 2026
f3b5acf
fix: keep malformed claim verification unavailable
seonghobae Aug 25, 2026
4ee6699
fix(ask): validate verification provider envelopes
seonghobae Aug 25, 2026
e1ebe50
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
seonghobae Aug 25, 2026
76ddb3f
feat(ask): ground answers at knowledge cutoff (#645)
seonghobae Aug 25, 2026
55b0d88
docs(gaps): refresh exact-head Ask stack (#646)
seonghobae Aug 25, 2026
87c073b
docs(gaps): advance stacked Ask head snapshot
seonghobae Aug 25, 2026
6b66327
fix(ci): keep lineage SQL static at call sites (#647)
seonghobae Aug 25, 2026
5c26240
docs(gaps): record dashboard head advance
seonghobae Aug 25, 2026
471eba0
fix(ci): clarify trusted ingestion SQL
seonghobae Aug 25, 2026
3a0014c
fix(ci): document bound static SQL suppression
seonghobae Aug 25, 2026
f2731af
docs(gaps): record static SQL repair head
seonghobae Aug 25, 2026
0d674cb
test(api): apply evidence search migration once
seonghobae Aug 25, 2026
582f8c6
fix(test): preserve late verification binding
seonghobae Aug 25, 2026
373db37
fix(ask): retain answers when verification fails
seonghobae Aug 25, 2026
be51f2f
fix(frontend): bound cutoff in local time
Aug 25, 2026
05b7cf8
feat(ask): constrain semantic query rewriting (#652)
seonghobae Aug 25, 2026
6cfba30
docs(gaps): refresh post-stack PR inventory
Aug 25, 2026
3e0986c
fix: keep ontology graph labels readable
Aug 25, 2026
d227edc
fix: contain semantic rewrite and share ABAC contract
Aug 25, 2026
ff096c1
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
c1db366
fix: bound ontology label rendering
Aug 25, 2026
6c0c437
Merge pull request #654 from ContextualWisdomLab/feat/ontology-long-l…
seonghobae Aug 25, 2026
e50f2a7
docs(gaps): refresh ontology stack evidence
Aug 25, 2026
5b502f9
test: apply dashboard projection migrations in schema fixture
Aug 25, 2026
25146ba
test: preserve current stack while applying schema fixture migrations
Aug 25, 2026
be7d0cf
Revert "test: apply dashboard projection migrations in schema fixture"
Aug 25, 2026
5184a31
docs(gaps): record live ontology publication
Aug 25, 2026
787f94a
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
3882739
docs(gaps): refresh current provenance stack head
Aug 25, 2026
66a9d32
docs: preserve current provenance stack updates
Aug 25, 2026
a145c17
docs(gaps): refresh hourly caller gate
Aug 25, 2026
7b50fb3
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
a946f87
docs(gaps): refresh current PR632 exact head
Aug 25, 2026
22ad71d
docs(gaps): refresh dashboard and provenance heads
Aug 25, 2026
3e3f0ea
docs(gaps): refresh latest dashboard and provenance heads
Aug 25, 2026
a0d4eb7
fix: bind Semgrep suppression to static SQL calls
Aug 25, 2026
fa5a423
feat: expose durable Global Ask through authenticated MCP
Aug 25, 2026
d314ed6
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
c86bf29
docs: refresh MCP stack parent evidence
Aug 25, 2026
1a9f968
Merge pull request #655 from ContextualWisdomLab/feat/mcp-global-ask-…
seonghobae Aug 25, 2026
a39a453
docs: record authenticated MCP stack merge evidence
Aug 25, 2026
61f2af7
fix: keep local MCP audience exact
Aug 25, 2026
ef13807
test: prove MCP trust boundary coverage
Aug 25, 2026
32d38df
fix(frontend): include pnpm build policy in image install
Aug 25, 2026
8b32299
docs: refresh exact open PR heads
Aug 25, 2026
b2438e0
docs: refresh latest PR heads
Aug 25, 2026
679df1d
docs: record latest queue head
Aug 25, 2026
c51e272
fix(mcp): preserve request lifecycle after body admission
Aug 25, 2026
4b3b350
fix: close scoped review race windows
Aug 25, 2026
e41476c
test(mcp): add authenticated concurrency evidence
Aug 25, 2026
3c7ca18
docs: refresh MCP and protected queue evidence
Aug 25, 2026
6001493
docs: record current protected PR queue
Aug 25, 2026
30354c3
docs: include latest knowledge cutoff PR
Aug 25, 2026
05b8f59
docs: refresh TEPP exact head evidence
Aug 25, 2026
9332b92
docs: record latest TEPP lifecycle head
Aug 25, 2026
e8e3930
docs: refresh global ask exact head
Aug 25, 2026
bae5122
docs: record TEPP receipt conflict fix
Aug 25, 2026
6fa6b78
docs: record dashboard exact head
Aug 25, 2026
b32e612
docs: record cutoff grounding repair
Aug 25, 2026
05b640a
docs: record live-only Ask grounding
Aug 25, 2026
8febced
docs: separate Naruon calendar and lineage contracts
Aug 25, 2026
87fce5c
docs: record Ask cutoff UI repair
Aug 25, 2026
a43b4bf
docs: include ontology readability PR
Aug 25, 2026
dfd2f8b
docs: refresh cutoff PR head
Aug 25, 2026
14a4b68
docs: include backend contract PRs
Aug 25, 2026
74a84dd
docs: refresh dashboard and ontology heads
Aug 25, 2026
30fce05
fix(migrations): reserve public verification sequence
Aug 25, 2026
b3c5b31
docs: record backend integration validation
Aug 25, 2026
38e9e77
docs: refresh current PR heads
Aug 25, 2026
12fe0b5
docs: record cutoff evidence boundary
Aug 25, 2026
dd7057f
docs: refresh PR 660 exact head
Aug 25, 2026
ea91b62
docs: refresh PR 658 exact head
Aug 25, 2026
66338ef
docs: record PR 661 in gap baseline
Aug 25, 2026
b11a256
docs: refresh current PR exact heads
Aug 25, 2026
9783798
docs: refresh status notice PR head
Aug 25, 2026
571a778
docs: refresh cutoff PR head
Aug 25, 2026
bc5f20c
docs: record dashboard PR transport head
Aug 25, 2026
652939c
docs: record TEPP status boundary PR
Aug 25, 2026
1ec0913
feat(ontology): traverse evidence-backed projects
Aug 25, 2026
1898e9b
docs: refresh current cutoff PR head
Aug 25, 2026
4796065
docs: refresh TEPP status PR head
Aug 25, 2026
82deea0
fix(ontology): keep project focus off team catalog
Aug 25, 2026
67a1994
docs: record ontology project PR
Aug 25, 2026
95fe2f8
docs(gaps): record project ontology runtime evidence
Aug 25, 2026
55bd4b0
fix(ontology): freeze project focus and labels
Aug 25, 2026
1025a94
docs: refresh ontology and TEPP exact heads
Aug 25, 2026
5855080
docs: refresh current ontology and TEPP heads
Aug 25, 2026
b71a7c8
docs(gaps): refresh exact open PR inventory
Aug 25, 2026
1986f93
fix(ontology): freeze project pagination evidence
Aug 25, 2026
b39cf5f
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
cf7df7b
docs(gaps): refresh ontology PR exact head
Aug 25, 2026
a41c9a4
docs(gaps): reconcile baseline provenance head
Aug 25, 2026
98f2acd
docs(gaps): correct hourly scheduler evidence
Aug 25, 2026
a7ab442
docs(perf): record authenticated concurrency evidence
Aug 25, 2026
d2423e5
docs(gaps): refresh current PR heads
Aug 25, 2026
026086f
docs(gaps): record authenticated concurrency evidence
Aug 25, 2026
22df3b6
docs(gaps): reconcile baseline branch head
Aug 25, 2026
110d3d2
fix(frontend): carry pnpm build policy into image (#665)
seonghobae Aug 25, 2026
4f1afeb
docs(gaps): inventory semantic unit PR
Aug 25, 2026
32c7d35
docs(gaps): record stacked frontend policy repair
Aug 25, 2026
f865107
Merge remote-tracking branch 'origin/pr660-latest' into fix/663-proje…
Aug 25, 2026
be361f1
fix(migrations): make replayed queue schemas idempotent
Aug 25, 2026
ab6bd80
security: document fixed ontology query fragment
Aug 25, 2026
73e7beb
docs(gaps): record integrated backend fixture repair
Aug 25, 2026
4af439d
fix(security): document static project query
Aug 25, 2026
7bb493c
docs(gaps): reconcile integrated stack heads
Aug 25, 2026
b469a6b
docs: reconcile ecosystem authority and ADR allocation
Aug 25, 2026
f73b4a1
docs(gaps): record semantic stack merge
Aug 25, 2026
dd7f4d2
docs(gaps): record ecosystem authority reconciliation
Aug 25, 2026
5ac296c
docs(architecture): move lineage arithmetic to owning libraries
Aug 25, 2026
856febb
docs(gaps): record arithmetic ownership ADR
Aug 25, 2026
1bd0a01
docs(gaps): reconcile current baseline branch head
Aug 25, 2026
357c91b
docs(gaps): record latest reconstruction test head
Aug 25, 2026
f138b2d
fix(ontology): label project mention relation
Aug 25, 2026
6287300
docs(gaps): record project relation label contract
Aug 25, 2026
3b4f15c
docs(gaps): record ontology label head
Aug 25, 2026
ff32571
docs(gaps): record latest ontology stack head
Aug 25, 2026
6bff354
docs(gaps): refresh exact open PR snapshot
Aug 25, 2026
c43fc9e
fix(semantic): expose SKOS preferred labels
Aug 25, 2026
e1f2476
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
cd2db0e
docs(gaps): reconcile closed tepp PR and queue count
Aug 25, 2026
f6a487a
docs(gaps): record tepp PR supersession
Aug 25, 2026
359c9c2
docs(gaps): record latest ontology stack merge
Aug 25, 2026
a0c246c
docs(gaps): record closed duplicate measurement PRs
Aug 25, 2026
2622453
docs(gaps): record evidence-bounded PR closures
Aug 25, 2026
f219c30
docs(gaps): record closed unanchored weight PR
Aug 25, 2026
d4f6edc
Merge remote-tracking branch 'refs/remotes/origin/pr663-race' into fi…
Aug 25, 2026
90c7f8f
docs(gaps): record latest race-branch head
Aug 25, 2026
e0aff6d
docs(gaps): record live ontology publication evidence
Aug 25, 2026
ac4a55a
docs(gaps): remove local lineage calculation PR
Aug 25, 2026
d213309
Merge remote-tracking branch 'refs/remotes/origin/pr663-after636-race…
Aug 25, 2026
995e4d8
docs(gaps): record live ontology publication head
Aug 25, 2026
e0cf4a2
test(ontology): require readable lookup labels
Aug 25, 2026
3584340
docs(gaps): record closed arithmetic reimplementation PR
Aug 25, 2026
20a2688
docs(gaps): record readable ontology label head
Aug 25, 2026
de4b158
docs(gaps): externalize residual map arithmetic
Aug 25, 2026
60e72d2
docs(gaps): record residual ownership closure
Aug 25, 2026
1a0f65f
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
2982175
docs(gaps): record residual-map ownership head
Aug 25, 2026
30f7e6b
docs(gaps): record latest ontology stack head
Aug 25, 2026
cdd9df1
docs(gaps): record closed residual map PR
Aug 25, 2026
020a0e8
docs(gaps): refresh exact PR heads
Aug 25, 2026
7195cd8
docs(gaps): mark self-advancing head snapshot
Aug 25, 2026
f56d35d
docs(gaps): record ontology and keyboard accessibility heads
Aug 25, 2026
16a81d0
docs(gaps): record responsive ontology audit
Aug 25, 2026
b2c1781
Merge branch 'feat/ontology-project-nodes-v2200' of https://github.co…
Aug 25, 2026
f018dc6
docs(gaps): record current ontology stack head
Aug 25, 2026
67993e5
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
9ba12a6
docs(gaps): record semantic stack reconciliation
Aug 25, 2026
b49b4e9
docs(gaps): record semantic stack reconciliation
Aug 25, 2026
3a030ad
fix(ontology): preserve process-unit scope for post focus
Aug 25, 2026
72e0ad3
Merge branch 'fix/global-ask-graph-fact-provenance' of https://github…
Aug 25, 2026
78f3f71
docs(gaps): record process unit focus head
Aug 25, 2026
7ceb496
docs(gaps): restore Rust arithmetic ownership
Aug 25, 2026
bdd4e29
docs(gaps): record provenance stack merge head
Aug 25, 2026
65c0def
Merge branch 'fix/global-ask-graph-fact-provenance' of https://github…
Aug 25, 2026
93a0a23
Merge branch 'feat/ontology-project-nodes-v2200' of https://github.co…
Aug 25, 2026
22435b8
docs(gaps): record latest ontology stack merge
Aug 25, 2026
b49b911
docs(gaps): reconcile RankWeave contract stack
Aug 25, 2026
7c7375d
docs(gaps): record RankWeave contract reconciliation
Aug 25, 2026
f43ee50
feat(ontology): project database rows to SHACL RDF
Aug 25, 2026
943ca3b
Merge branch 'feat/ontology-project-nodes-v2200' of https://github.co…
Aug 25, 2026
ff0c217
docs(gaps): record latest ontology stack head
Aug 25, 2026
aa521ab
docs(gaps): record first RankWeave Rust slice
Aug 25, 2026
cd27134
Merge remote-tracking branch 'origin/pr-659-audit' into HEAD
Aug 25, 2026
3df75a1
fix(ontology): name project nodes in explorer copy
Aug 25, 2026
9caad87
Merge branch 'feat/ontology-project-nodes-v2200' of https://github.co…
Aug 25, 2026
56d1b4e
docs(gaps): record first RankWeave Rust slice
Aug 25, 2026
2488f0f
docs(gaps): reconcile ontology UX candidate evidence
Aug 25, 2026
3146170
docs(gaps): record ontology UX evidence head
Aug 25, 2026
702adf2
docs: refresh exact head for baseline queue
Aug 25, 2026
6bb096a
fix(vision): remove sampled region coverage heuristic
Aug 25, 2026
d97614f
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
0b45c41
test(ontology): keep doubles aligned with cutoff API
Aug 25, 2026
f369ca0
docs(gaps): refresh RankWeave Rust head evidence
Aug 25, 2026
656a652
docs: record image evidence PR in gap baseline
Aug 25, 2026
88a60c6
docs(gaps): record pinned RankWeave build head
Aug 25, 2026
eb2f9b7
Merge remote-tracking branch 'origin/fix/no-region-coverage-heuristic…
Aug 25, 2026
215d4e6
docs(gaps): reconcile PR 632 exact head
Aug 25, 2026
85b963f
fix(ontology): enforce label invariant in optimized mode
Aug 25, 2026
f0727f8
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
ddb3142
test(ontology): follow temporal visibility contract
Aug 25, 2026
8971697
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
d3209c5
fix(ask): skip unused cutoff embeddings
Aug 25, 2026
f8c7895
Merge pull request #666 from ContextualWisdomLab/fix/no-region-covera…
seonghobae Aug 25, 2026
0c16b33
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
cb4cd1a
fix(ontology): scope project candidates and temporal evidence
Aug 25, 2026
7fcf050
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
2c158c4
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
db11629
test(ontology): standardize module imports
Aug 25, 2026
d7cd7ab
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
8aea526
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
702513f
docs: record stacked vision merge in baseline
Aug 25, 2026
8eaa2c1
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
e4cc240
test(migrations): preserve global ask replay order
Aug 25, 2026
f924b5a
fix(mcp): emit quota retry header end to end
Aug 25, 2026
abbe724
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
834d2c4
docs(gaps): record scoped project exact head
Aug 25, 2026
a405911
fix(mcp): close review contract gaps
Aug 25, 2026
210bc9a
Merge remote-tracking branch 'origin/fix/global-ask-graph-fact-proven…
Aug 25, 2026
c661f2b
fix(ontology): unify multilingual node IRIs
Aug 25, 2026
efbfb5d
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
ab330a0
docs(gaps): refresh exact open heads
Aug 25, 2026
4fc90ed
fix(ontology): preserve JSON-LD assertion semantics
Aug 25, 2026
81bb829
Merge remote-tracking branch 'origin/feat/ontology-project-nodes-v220…
Aug 25, 2026
6fd2f70
Merge remote-tracking branch 'origin/main' into HEAD
Aug 25, 2026
d3bb934
Merge current protected main into project ontology branch
Aug 25, 2026
e65fd29
fix(ask): avoid empty cutoff grounding claim
Aug 25, 2026
f5e7e50
fix(mcp): bound content length parsing explicitly
Aug 25, 2026
4bb3ab5
fix(mcp): classify oversized decimal lengths before integer parsing
Aug 25, 2026
d5edd2b
Merge concurrent MCP admission fix
Aug 25, 2026
52afff1
test: mirror concurrent migration semantics
Aug 25, 2026
7ac1483
fix(compose): start ask worker with mcp profile
Aug 25, 2026
74b9a1d
test(schema): execute concurrent indexes like psql
Aug 26, 2026
f616887
test(schema): preserve authenticated transactions in integration fixture
Aug 26, 2026
0cb9592
docs(adr): avoid open-stack identifier collisions
Aug 26, 2026
4ded3d2
docs(adr): align renumbered titles
Aug 26, 2026
885f27f
fix(ontology): honor edge cutoff and localize actions
Aug 26, 2026
ee795ac
fix(ontology): seal focus evidence at cursor snapshot
Aug 26, 2026
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
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Copy to .env to override. Every value below already has a working
# default baked into docker-compose.yml (see ${VAR:-default} references) --
# `docker compose up` succeeds from a clean checkout with no .env file at
# all. These defaults are throwaway local-dev-only credentials, not
# all for the default profile. The optional MCP profile requires measured
# quota inputs below. Other defaults are throwaway local-dev-only credentials, not
# production secrets; see docs/adr/0001-demo-identity-and-data-boundary.md.

# Host ports deliberately avoid each service's own default (5432, 6379,
Expand All @@ -27,6 +28,12 @@ OIDC_AUDIENCE=lineageweave-api

BACKEND_PORT=18420

# Optional authenticated MCP profile. The quota pair is mandatory when the
# profile is enabled and must come from that deployment's k6 capacity evidence.
MCP_ALLOWED_ORIGINS=
MCP_RATE_LIMIT_REQUESTS=
MCP_RATE_LIMIT_WINDOW_SECONDS=

# Optional. Empty = every LLM/vision channel is unavailable (Null client,
# dropped and renormalized -- never a placeholder score). Point these at a
# running contextual-orchestrator to turn the channels on.
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.d/2.18.1-current-contract-mcp-global-ask.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Added

- Added an authenticated Streamable HTTP MCP adapter that queues and reads the
same durable Global Ask jobs as REST, with exact-resource OAuth, bounded
pre-auth request admission, owner/affiliation scope preservation, and a
fail-closed distributed quota whose capacity inputs are deployment evidence.
22 changes: 22 additions & 0 deletions CHANGELOG.d/2.20.0-project-ontology-neighborhood.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 2.20.0 — Evidence-backed Project ontology neighborhoods

- Added canonical Project nodes and `mentionsProject` relations to the bounded
ontology API, JSON-LD/CSV projections, and accessible explorer.
- Bound Project labels and evidence to the request cutoff and sealed cursor
snapshot; hidden or unavailable evidence remains absent.
- Scoped unresolved Project candidate identifiers to their evidence Post so
equal names cannot silently merge across records.
- Unified RDF and JSON-LD node IRI encoding for multilingual Project keys.
- Corrected JSON-LD edge semantics to emit both the direct assertion and its
evidence-bearing RDF reification.
- Aligned JSON-LD system and validity time with the API contract and exposed
exact validity/evidence values in the accessible explorer table.
- Made synthetic channel-weight seeding record one explicit cutoff/estimate
instant, avoiding transaction-start clock violations during long test runs.
- Included process-unit scope in Post-focus authorization rows so private
ontology neighborhoods fail closed instead of crashing during ABAC checks.
- Reconciled the Project ontology work with the Global Ask provenance, public
verification, semantic rewrite, knowledge-cutoff, and MCP stack.
- Added the deterministic joined-row RDF projector used by SHACL acceptance,
including the direct relation, complete reified statement, evidence,
confidence, creation time, and PROV derivation.
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: up down logs smoke seed ps load-http
.PHONY: up down logs smoke seed ps load-http load-mcp

# Keep provider credentials outside the repository. Compose interpolation must
# read the same home env file as the orchestrator container's env_file.
Expand Down Expand Up @@ -35,4 +35,12 @@ seed:
load-http:
@test -n "$${LINEAGEWEAVE_VUS:-}" || { echo "LINEAGEWEAVE_VUS is required" >&2; exit 1; }
@test -n "$${LINEAGEWEAVE_DURATION:-}" || { echo "LINEAGEWEAVE_DURATION is required" >&2; exit 1; }
k6 run --vus "$${LINEAGEWEAVE_VUS}" --duration "$${LINEAGEWEAVE_DURATION}" scripts/k6_http_e2e.js
@test -n "$${LINEAGEWEAVE_REQUEST_TIMEOUT:-}" || { echo "LINEAGEWEAVE_REQUEST_TIMEOUT is required" >&2; exit 1; }
k6 run -e REQUEST_TIMEOUT="$${LINEAGEWEAVE_REQUEST_TIMEOUT}" --vus "$${LINEAGEWEAVE_VUS}" --duration "$${LINEAGEWEAVE_DURATION}" scripts/k6_http_e2e.js

# Authenticated MCP measurement with operator-supplied observation bounds.
load-mcp:
@test -n "$${LINEAGEWEAVE_VUS:-}" || { echo "LINEAGEWEAVE_VUS is required" >&2; exit 1; }
@test -n "$${LINEAGEWEAVE_DURATION:-}" || { echo "LINEAGEWEAVE_DURATION is required" >&2; exit 1; }
@test -n "$${LINEAGEWEAVE_REQUEST_TIMEOUT:-}" || { echo "LINEAGEWEAVE_REQUEST_TIMEOUT is required" >&2; exit 1; }
k6 run -e REQUEST_TIMEOUT="$${LINEAGEWEAVE_REQUEST_TIMEOUT}" --vus "$${LINEAGEWEAVE_VUS}" --duration "$${LINEAGEWEAVE_DURATION}" scripts/k6_mcp_e2e.js
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ carrying `corp_code` / `pu_code` as token claims -- these are throwaway
local-dev credentials in a locally-run realm, never the org's real Keyverse
tenant (see ADR 0001 for why).

Host ports (15432, 16379, 18080, 18420) deliberately avoid each service's
Host ports (15432, 16379, 18080, 18001, 18420) deliberately avoid each service's
own default -- a dev machine commonly already runs its own
Postgres/Redis/local server on those. Override via `.env` (copy
`.env.example`) or inline if even those collide, e.g.
Expand All @@ -156,6 +156,18 @@ make seed # scripts/seed_demo_data.py: inserts synthetic corp/account/post
curl http://localhost:18420/healthz
```

The optional authenticated MCP resource server submits and reads the same
durable Global Ask jobs as REST. Enable it only with quota values established
by the deployment's k6 capacity evidence; the service intentionally has no
guessed request/window defaults:

```bash
MCP_RATE_LIMIT_REQUESTS=<measured-count> \
MCP_RATE_LIMIT_WINDOW_SECONDS=<measured-window> \
docker compose --profile mcp up mcp
# Streamable HTTP resource: http://localhost:18001/mcp
```

`GET /api/posts`, `GET /api/posts/{post_id}`,
`GET /api/posts/{post_id}/keymen`, `GET /api/keymen/{person_id}/related`,
`GET /api/posts/{post_id}/affiliate-tree`,
Expand Down
32 changes: 23 additions & 9 deletions backend/app/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,10 @@ def has_permission(self, permission_code: str) -> bool:
return permission_code in self.permission_codes


def _decode_access_token(token: str, settings: Settings) -> dict:
"""Validate signature, issuer, resource audience, time claims, and subject."""
def decode_access_token(
token: str, settings: Settings, *, audience: str | None = None
) -> dict:
"""Validate a token for the REST or an explicit resource audience."""
required_claims = ["exp", "sub"]
if settings.keyverse_claim_binding_required:
required_claims.insert(1, "iat")
Expand All @@ -136,7 +138,7 @@ def _decode_access_token(token: str, settings: Settings) -> dict:
key=_signing_key(settings, token),
algorithms=["RS256"],
issuer=settings.oidc_issuer,
audience=settings.oidc_audience,
audience=settings.oidc_audience if audience is None else audience,
leeway=settings.oidc_clock_skew_seconds,
options={"require": required_claims},
)
Expand All @@ -150,6 +152,11 @@ def _decode_access_token(token: str, settings: Settings) -> dict:
return claims


def _decode_access_token(token: str, settings: Settings) -> dict:
"""Validate a REST bearer token against the configured API audience."""
return decode_access_token(token, settings)


def _keyverse_account_claims(claims: dict) -> tuple[str, str, list[str]]:
"""Return Keyverse's atomic account scope, rejecting ambiguous wire shapes."""
organization = claims.get("org")
Expand Down Expand Up @@ -177,13 +184,10 @@ def _keyverse_account_claims(claims: dict) -> tuple[str, str, list[str]]:
return organization, workspace, [role.strip() for role in roles]


async def get_current_account(
credentials: HTTPAuthorizationCredentials = Depends(_bearer_scheme),
pool: asyncpg.Pool = Depends(get_pool),
async def resolve_current_account(
pool: asyncpg.Pool, claims: dict, settings: Settings
) -> CurrentAccount:
"""Resolve the bearer token to a provisioned ``user_account`` row."""
settings = load_settings()
claims = _decode_access_token(credentials.credentials, settings)
"""Resolve verified claims to database-owned scope and permissions."""
subject = claims["sub"]
keyverse_scope = (
_keyverse_account_claims(claims)
Expand Down Expand Up @@ -269,3 +273,13 @@ async def get_current_account(
process_unit_ids=frozenset(str(row["process_unit_id"]) for row in process_rows),
permission_codes=frozenset(row["permission_code"] for row in permission_rows),
)


async def get_current_account(
credentials: HTTPAuthorizationCredentials = Depends(_bearer_scheme),
pool: asyncpg.Pool = Depends(get_pool),
) -> CurrentAccount:
"""Resolve the bearer token to a provisioned ``user_account`` row."""
settings = load_settings()
claims = _decode_access_token(credentials.credentials, settings)
return await resolve_current_account(pool, claims, settings)
78 changes: 68 additions & 10 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import math
import os
from dataclasses import dataclass
from dataclasses import dataclass, field

# Hard ceiling on one Global Ask job's answer computation, shared with the
# worker in global_ask_queue.py so config validation and execution can never
Expand Down Expand Up @@ -65,6 +65,16 @@ class Settings:
naruon_calendar_service_token: str
rankweave_disabled: bool
ontology_source_cursor_secret: str
mcp_resource_url: str = "http://localhost:18001/mcp"
mcp_audience: str = "http://localhost:18001/mcp"
mcp_required_scopes: list[str] = field(default_factory=list)
mcp_allowed_hosts: list[str] = field(
default_factory=lambda: ["localhost:*", "127.0.0.1:*", "mcp:8001"]
)
mcp_allowed_origins: list[str] = field(default_factory=list)
mcp_max_request_bytes: int = 65_536
mcp_rate_limit_requests: int | None = None
mcp_rate_limit_window_seconds: int | None = None

@property
def keycloak_jwks_uri(self) -> str:
Expand All @@ -83,7 +93,9 @@ def _validated_answer_timeout(raw: str) -> float:
try:
value = float(raw)
except ValueError as exc:
raise ValueError("ORCHESTRATOR_ANSWER_TIMEOUT_SECONDS must be a number") from exc
raise ValueError(
"ORCHESTRATOR_ANSWER_TIMEOUT_SECONDS must be a number"
) from exc
if not math.isfinite(value) or not 0 < value < GLOBAL_ASK_JOB_DEADLINE_SECONDS:
raise ValueError(
"ORCHESTRATOR_ANSWER_TIMEOUT_SECONDS must be a finite number greater"
Expand All @@ -92,6 +104,20 @@ def _validated_answer_timeout(raw: str) -> float:
return value


def _optional_positive_int(name: str) -> int | None:
"""Parse an optional positive deployment integer without inventing a default."""
raw = os.environ.get(name, "").strip()
if not raw:
return None
try:
value = int(raw, 10)
except ValueError as exc:
raise ValueError(f"{name} must be a base-10 integer") from exc
if value <= 0:
raise ValueError(f"{name} must be positive")
return value


def load_settings() -> Settings:
"""Read Settings from the environment, with local-dev defaults only."""
keycloak_base_url = os.environ.get("KEYCLOAK_BASE_URL", "http://localhost:18080")
Expand All @@ -103,7 +129,9 @@ def load_settings() -> Settings:
keyverse_issuer = os.environ.get("KEYVERSE_ISSUER", "").strip()
generic_oidc_issuer = os.environ.get("OIDC_ISSUER", "").strip()
external_oidc = bool(keyverse_issuer or generic_oidc_issuer)
oidc_issuer = (keyverse_issuer or generic_oidc_issuer or keycloak_issuer).rstrip("/")
oidc_issuer = (keyverse_issuer or generic_oidc_issuer or keycloak_issuer).rstrip(
"/"
)
oidc_client_id = (
os.environ.get("KEYVERSE_CLIENT_ID", "").strip()
or os.environ.get("OIDC_CLIENT_ID", "").strip()
Expand All @@ -119,9 +147,13 @@ def load_settings() -> Settings:
"do not infer a resource-server audience from the browser client id"
)
oidc_audience = configured_audience or "lineageweave-api"
oidc_discovery_uri = os.environ.get("KEYVERSE_DISCOVERY_URI", "").strip() or os.environ.get(
"OIDC_DISCOVERY_URI", ""
mcp_resource_url = os.environ.get(
"MCP_RESOURCE_URL", "http://localhost:18001/mcp"
).strip()
oidc_discovery_uri = (
os.environ.get("KEYVERSE_DISCOVERY_URI", "").strip()
or os.environ.get("OIDC_DISCOVERY_URI", "").strip()
)
if not oidc_discovery_uri:
discovery_base = oidc_issuer if external_oidc else keycloak_base_url
oidc_discovery_uri = (
Expand Down Expand Up @@ -161,7 +193,9 @@ def load_settings() -> Settings:
keyverse_claim_binding_required=bool(keyverse_issuer),
frontend_origins=[
origin.strip()
for origin in os.environ.get("FRONTEND_ORIGINS", "http://localhost:5173").split(",")
for origin in os.environ.get(
"FRONTEND_ORIGINS", "http://localhost:5173"
).split(",")
if origin.strip()
],
orchestrator_base_url=os.environ.get("ORCHESTRATOR_BASE_URL", ""),
Expand All @@ -178,9 +212,33 @@ def load_settings() -> Settings:
naruon_calendar_service_token=os.environ.get(
"NARUON_CALENDAR_SERVICE_TOKEN", ""
).strip(),
rankweave_disabled=os.environ.get("RANKWEAVE_DISABLED", "")
.strip()
.lower()
rankweave_disabled=os.environ.get("RANKWEAVE_DISABLED", "").strip().lower()
in {"1", "true", "yes", "on"},
ontology_source_cursor_secret=os.environ.get("ONTOLOGY_SOURCE_CURSOR_SECRET", "").strip(),
ontology_source_cursor_secret=os.environ.get(
"ONTOLOGY_SOURCE_CURSOR_SECRET", ""
).strip(),
mcp_resource_url=mcp_resource_url,
mcp_audience=os.environ.get("MCP_AUDIENCE", mcp_resource_url).strip(),
mcp_required_scopes=[
item.strip()
for item in os.environ.get("MCP_REQUIRED_SCOPES", "").split(",")
if item.strip()
],
mcp_allowed_hosts=[
item.strip()
for item in os.environ.get(
"MCP_ALLOWED_HOSTS", "localhost:*,127.0.0.1:*,mcp:8001"
).split(",")
if item.strip()
],
mcp_allowed_origins=[
item.strip()
for item in os.environ.get("MCP_ALLOWED_ORIGINS", "").split(",")
if item.strip()
],
mcp_max_request_bytes=_optional_positive_int("MCP_MAX_REQUEST_BYTES") or 65_536,
mcp_rate_limit_requests=_optional_positive_int("MCP_RATE_LIMIT_REQUESTS"),
mcp_rate_limit_window_seconds=_optional_positive_int(
"MCP_RATE_LIMIT_WINDOW_SECONDS"
),
)
Loading
Loading