feat(knowledge): add Amazon Bedrock Knowledge Base retrieval sources - #8985
feat(knowledge): add Amazon Bedrock Knowledge Base retrieval sources#8985jingchaodev wants to merge 1 commit into
Conversation
686d06f to
05d5325
Compare
Design Review (Fable 5, fork) — 🟡 CONCERNSDesign-level review of All design premises the PR rests on verify against the base tree: the sandbox bind-mount does pin the consent file's inode ( Design-Verdict: CONCERNS Sound feature, but a temporary one-grant limit bought permanent cross-subsystem coupling, and per-search STS probes can silently starve the remote leg. Watch
[DESIGN-REVIEWED] 1f77fa8 |
First Principles Review (Fable 5, fork) — 🟡 CONCERNSPremise-level review of All checks done — the sandbox inode-pin claim, the revoke call-site sibling count (2 evidence-based sites, both converted; the 2 operator-initiated ones correctly untouched), the screenshot-harness convention (435 sibling scripts, 1288 committed PNGs), and the race pins added in First-Principles-Verdict: CONCERNS The one-account target-conflict subsystem guards a cause the author names and defers — the consent store's single-grant-per-service keying — and asks you to sign off on it. Not justified as shipped
What this change shipsIntent: point knowledge search at an existing Bedrock Knowledge Base in the user's own AWS account (linked issue #7947) — ADDITION.
Item 4's zero option is real: base Watch
[FIRST-PRINCIPLES-REVIEWED] 1f77fa8 |
UX Review (Fable 5, fork) — 🟡 CONCERNSUX-level review of I have everything I need: the full frontend diff, the new strings across locales, the backend error strings that surface in the form, the base UX-Verdict: CONCERNS Well-shaped consent-in-form flow, but no cold reader has seen any of it (fork lane), and first-failure error copy is raw AWS exception codes. Watch
Evidence gaps
Suggestions
[UX-REVIEWED] 1f77fa8 |
GPT 5.6 Review (fork) — ✅ no blocking findingsReviewed Review detailsNo findings. |
05d5325 to
5eab1e8
Compare
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed Review detailsI verified the consent gate ( No findings. [OPUS-REVIEWED] 1f77fa8 |
|
First Principles round 1 — accepted and applied (head 5eab1e8): |
5eab1e8 to
207fb57
Compare
|
Round 3 — all findings accepted and fixed (head 207fb57):
Connector suite 21/21, black/isort/flake8/mypy/tsc green, SourcesList suites 38/38. |
207fb57 to
3798cb3
Compare
|
Round 4 — all four items fixed (head 3798cb3):
Connector suite 22/22; black gate + baseline check, flake8, mypy green. |
3798cb3 to
f519b8d
Compare
|
Round 5 — both blockers and both findings fixed (head f519b8d):
Connector suite 26/26 (74 with neighbors), black gate + new-files check, flake8, isort, mypy (1302 files) all green; single commit. |
f519b8d to
f66dc61
Compare
|
Advisory rounds (Design / UX / First Principles) — addressed (head f66dc61):
Backend 26/26, frontend SourcesList suites 52/52, i18n 19/19, tsc, black/sync-io/brand ratchets green; single commit. |
f66dc61 to
cc3794f
Compare
|
Round 7 — security finding fixed (head cc3794f): the remote query is now passed through |
cc3794f to
1459ff6
Compare
|
Round 8 — security finding fixed as prescribed (head 1459ff6): Bedrock KB retrieval is now registered with |
1459ff6 to
12438f9
Compare
|
Round 27 (head 2d87683): GPT's crash finding on the new internal route, legitimate and exactly right — the handler read a bare |
|
Round 28 (head d9b4086): GPT's non-dict JSON body finding on the internal route — legitimate, fixed with an explicit |
|
Round 29 (head 4d86d7d): both GPT findings on the internal route, legitimate and fixed.
Route test extended: no-marker → 403; bounded-variant stub raising → fail-open 200/[]. Suites 138/138, gates green, single commit. |
|
Round 30 (head 8b21b0c): the two backend shards' |
|
Round 31 — GPT ✅ and Opus ✅ clean again; the three advisory lanes addressed (head d57a609):
Suites 138/138, tsc + form tests green, screenshots re-captured, single commit. |
|
Round 32 (head 4f1b5a1): GPT's last crack in the stale-revocation class, fixed as prescribed — the targeted consent GET now captures the grant BEFORE its probe and |
|
Round 33 (head a78dbdb): GPT's validate mis-classification, legitimate and fixed — the probe loop treated the connector's own consent refusals (a |
|
Round 34 (head 1f4c054): the Windows shard's |
|
Round 35 (head 2b31692): GPT's base/secret pairing finding on the MCP fetch — fixed with the atomic-pair shape it asked for. |
|
Round 36 (head e10af5d): the E2E i18n-render failure was my commit carrying a pre-rebase |
|
Round 37 (head a1c8ff6): GPT's missing-audit finding on the internal route's 403, legitimate — every peer internal-auth handler SEL-logs its denial and this refusal is reachable by an ordinary cookie session, so the silent 403 was invisible to |
|
Round 38 (head d4b748f): GPT's refined pairing finding — fixed with exactly the mcp_core surface it named, which turns out to be purpose-built for this: |
|
Round 39 — GPT ✅ / Opus ✅ third clean set; advisory items taken (head 1abd52a):
Suites 140/140, form tests 4/4, tsc green, 5 screenshots re-captured, single commit. |
|
Round 40 — FP upgraded to ✅ PASS; Design's enforcement suggestion taken (head 0cb409c):
Suites 141/141, form tests 4/4, gates green, single commit. |
|
Round 41 (head 70b7622): the changed-passthrough i18n gate caught my hi catalog's |
|
Round 42 (head babd9aa): the dead-keys ratchet caught the orphaned |
|
Round 43 — GPT ✅ / Opus ✅ hold; the three advisory lanes' takeable items taken (head 65e44b1):
Suites 141/141, form tests 4/4, tsc green, screenshots re-captured, single commit. |
|
Round 44 (head a16b799): the Fork workflow-change guard was right — the diff touched |
|
Round 45 — best round yet, one takeable UX item taken (head 8784607). On a16b799 the round settled fully clean: zero CI failures, GPT ✅, Opus ✅, First-Principles ✅ PASS, Design/UX advisory-CONCERNS.
|
|
Round 46 — GPT's blocking finding fixed; the advisory taken too (head 4c891b8):
|
|
Round 47 (head 131202b): Semgrep flagged the round-46 guard helper's returned f-string as |
|
Round 48 — Design ✅ PASS (first); FP subtraction + both UX takeables taken (head eef6d14):
Pseudolocale regenerated (12920 keys); dead-keys baseline preserved; suites 7/7 + 146/146; single commit. |
|
Round 49 — GPT's two blocking findings fixed (#19, #20) (head d1370f5). Design ✅ PASS and FP ✅ PASS both held on eef6d14.
|
|
Round 50 — GPT's blocking finding fixed (#21) + both advisories + FP's subtraction (head fc44ff9):
|
|
Round 51 — GPT ✅ clean; UX takeable taken; one Design suggestion DECLINED with citation (head cc81907):
|
|
Round 52 (head 1af66fc):
|
|
Rebased onto main Conflicts: none, clean rebase. I did check the two spots main moved under you: merged #9032 rewrote Gates run locally: Please review the result. A maintainer push makes the maintainer the last pusher, so under the repo's last-push rule this now needs a second approver. Reply if anything looks wrong. |
A bedrock_kb source points at existing Bedrock Knowledge Bases in the user's own AWS account and is queried live at search time - no local ingestion, no stored credentials (profile name only, resolved through the standard AWS chain at call time). Results merge into local_knowledge_search and the dashboard's search-for-context by rank, under a 3s fail-open budget, so a slow or broken KB never breaks knowledge search. Retrieval tries vectorSearchConfiguration and falls back to managedSearchConfiguration when the ValidationException names it: MANAGED-type KBs reject the vector key. Citations come from the source_uri metadata attribute so hits link to original documents rather than S3 objects. boto3 ships as a new optional [bedrock] extra. Closes kirodotdev#7947
|
@bolichen97 Thank you for the audit rebase — reviewed it, and both overlap calls are right: the remote leg still belongs immediately after the |
Problem / Motivation
The Knowledge library can only search content KiroCrew ingests itself
(files, folders, vaults). Teams that already maintain a curated Amazon
Bedrock Knowledge Base — often gigabytes of code and docs indexed in their
own AWS account — cannot point KiroCrew at it: the only options are
re-ingesting the whole corpus locally or leaving the KB unreachable from
knowledge search. Requested in #7947.
Why it matters
local embedding cost) for corpora that are already indexed.
residency), addressing the security-orchestrator grounding use case Configurable knowledge backend: use an Amazon Bedrock Knowledge Base as a retrieval source #7947
describes.
What changed (motivation → approach → change)
Goal: registered Bedrock KBs should answer through the SAME retrieval
surfaces as local knowledge —
local_knowledge_search(MCP) and thedashboard's search-for-context — without touching the local index.
Approach: a new
bedrock_kbsource type that holds no local items and isqueried live at search time, alongside (not replacing) the local store —
answering the merge/reach questions the issue triage left open. Alternatives
considered: mirroring the KB's S3 bucket into local ingestion (rejected:
duplicates storage + embeddings and drifts between syncs) and a network leg
inside
HybridRetriever.search(rejected: that path is sync and thescoped-search exhaustion loop would multiply remote round trips); and
save-first consent (persist the source in a pending state so its target
lives in config and the consent card reuses the config-read path every
other service uses — rejected: a pending-but-unusable source is visible in
every consumer of the sources table (search legs, the dashboard list, the
orphan sweep) and each would need a pending-state carve-out, trading the
request-target TOCTOU surface, which is closed by construction at two
choke points, for a lifecycle state smeared across every reader; the
grantable-card-in-form keeps the unsaved target where the user is typing
it and the store free of half-born rows).
The change:
knowledge/connectors/bedrock_kb.py—BaseConnectorsubclass plus asearch()retrieval path:bedrock-agent-runtime Retrieveper configuredKB (
kb_idsaccepts ids or full ARNs for cross-account KBs), merged byrelevance score. Retrieval tries
vectorSearchConfigurationand fallsback to
managedSearchConfigurationwhen the ValidationException names it— MANAGED-type KBs (the current console/crawler default) reject the vector
key, so without the fallback the first call fails. Result URLs come from
the
source_urimetadata attribute (falling back to the reservedx-amz-bedrock-kb-source-uri, then the storage location), so citationspoint at original documents rather than S3 objects.
Bedrock KB retrieval is a consent-gated paid service (
aws_consent.SERVICE_BEDROCK_KB): the add-time probe and every retrieval require an account-bound grant for (service, profile, region), refusing fail-closed, so a repointed profile cannot receive so much as an access check.Config carries no secrets:
kb_ids,region, optionalprofile; credentials resolve through the standard AWS chain for the namedprofile at call time.
validate_configruns a 1-result live probe per KBso a bad id/region/profile is refused when the source is added.
Query path:
search_remote_sources_bounded— a shared worker pool under awall-clock budget (
REMOTE_SEARCH_TIMEOUT_SECS) over bounded boto connect/read timeouts, failing OPENto local-only results — called from
local_knowledge_searchandsearch_for_context, then interleaved by rank (merge_by_rank): local RRFscores and Bedrock relevance scores are not comparable, so a raw score sort
would always favor one leg wholesale. Installs with no
bedrock_kbsources pay one indexed SELECT and no thread.
boto3 ships as a new optional
[bedrock]extra (same pin as[voice-aws]),lazily imported; without it the source type reports the missing extra.
Frontend: the Sources tab's Add Source dialog gains a Bedrock KB type with
KB-IDs/region/profile fields (namespace picker hidden — namespaces scope
ingested items). 9 new i18n keys across
en.manual.json, all 11translations, and the regenerated pseudolocale.
Spec:
docs/system-specs/modules/knowledge.mddocuments the remote-sourcesemantics in the same commit.
Three changes reach beyond the new source type, all required by it:
add_sourcenow runs every connector'svalidate_configviaasyncio.to_thread(a live network probe must not block the event loop; folder validation is filesystem-touching and benefits equally);bedrock-kb://joins the non-filesystem URI prefixes exempt from the local-path sensitive check (it is a logical handle, not a path); and drift revocation inaws_consent(reconcile_drift+ the newrevoke_if_matches) now judges only the exact grant its probe evidence is about, which also applies to Polly/Transcribe/S3/Cost Explorer — derived from review-found races on the new targeted consent GET (both race shapes are test-pinned), and strictly narrowing: a grant is never revoked on evidence about a different target or a predecessor grant.Deliberate v1 trade-off flagged for maintainer sign-off: every remote search pays the uncached STS consent probe (the drift window is exactly what the uncached probe closes, per
authorize's documented reasoning). A short-TTL verified-account cache would bound rather than eliminate that window and is listed as a follow-up decision.Deliberate v1 trade-off flagged for maintainer sign-off: the target-conflict subsystem (two gates, the shared
target_change_lock, three refusal codes) is permanent-looking complexity defending the TEMPORARY one-account limit of the single-grant-per-service consent store. Keying grants by (service, profile, region) — the declared follow-up — makes Bedrock natively multi-account and deletes that subsystem outright.Remote hits carry a relevance floor (
MIN_REMOTE_SCORE = 0.25): Bedrock Retrieve returns nearest neighbors unconditionally, so an off-topic KB would otherwise occupy merged result slots on every search.Tests
test/test_bedrock_kb_connector.py(20 tests, mocked client, no network):the managed fallback fires only on the error naming it; non-managed errors
propagate; multi-KB fan-out merges by score and survives partial failure;
total failure raises; citation precedence (source_uri → reserved key →
location); validate_config field checks, per-KB probing, auth-failure
refusal, throttle-as-accessible, missing-extra message; store enumeration +
source_id scoping + per-source fail-open; the bounded entry's no-sources
fast path, timeout fail-open, and error swallowing; rank interleave; the
connector never syncs.
website/src/test/SourcesList.bedrock.test.tsx(3 tests): the type buttonrenders, submit gates on KB ids + region, the POST body carries the derived
bedrock-kb://uri and kb properties with nothing credential-shaped, andfields reset after a successful add.
Manual verification
Verified end-to-end against a real Bedrock Managed Knowledge Base
(~45K documents) with profile-based credentials: the raw vector-config call
is rejected (confirming the fallback is load-bearing), the connector returns
scored results whose URLs are the original document links (not s3://), and
the add-source live probe passes. The screenshot harness
(
website/scripts/capture-bedrock-kb-source.mjs) also asserts the form'spresence, submit gating, and hint text against the built SPA.
Screenshots / video
Add Source dialog, Bedrock KB type (dark, empty — submit disabled):

Filled (dark) — submit enabled:

Filled (light)
Connected source row (Live badge, no sync/staleness controls):
Consent granted — the receipt inside the form:
Related Issues
Closes #7947
Pattern harvest
N/A — feature PR, not a fix/revert.