This guide covers practical modifications developers commonly need to make: styling, authentication, API extensions, frontend components, LLM configuration, database migrations, and deployment. For guardrail configuration, see Docs/BEDROCK_GUARDRAILS.md.
- Modifying Colors and Styles
- Admin & Public Token
- Using External Identity Providers (Enterprise SSO)
- Extending the API
- Modifying Frontend Components
- Changing Website License (Footer)
- Configuring LLM Models
- Database Schema Changes (Migrations)
- Message/Token Limit Management
- Data Ingestion Modifications
- Adding Re-Ranking to Retrieval
- Deployment & Testing
- Encryption & KMS Keys
- Troubleshooting & Best Practices
The frontend uses Tailwind and CSS variables for theme colors. Primary variables are defined in frontend/src/index.css.
Example (change the primary brand color and sidebar background):
/* frontend/src/index.css */
:root {
--primary: rgb(23, 68, 103);
--sidebar: rgb(23, 68, 103);
}
.dark {
--primary: rgb(23, 68, 103);
--sidebar: rgb(23, 68, 103);
}For component-specific overrides, search for hex codes or Tailwind classes directly in frontend/src/components/.
All users — both regular users and admins — authenticate via Microsoft Entra ID federated through AWS Cognito. There is no anonymous or public access.
- Regular users: Sign in with Microsoft via the landing page (
frontend/src/pages/LandingPage.tsx). Cognito issues a JWT which the frontend stores and passes on all API calls. - Admin users: Same Entra ID sign-in flow, but must be a member of the
adminCognito group. Admin-only APIs are restricted by the admin authorizer inOpenAPI_Swagger_Definition.yamlandcdk/lib/api-stack.ts. Once signed in, admins see a Mode switcher in the header to toggle between user and admin views.
Key files:
cdk/lib/api-stack.ts— Cognito UserPool, Entra ID OIDC identity provider, and API authorizer setupfrontend/src/functions/authService.js—signInWithRedirectand session managementfrontend/src/components/ProtectedRoute.tsx— route-level auth and admin group check
The application is pre-configured with Microsoft Entra ID as the sole identity provider. To swap it for a different OIDC/SAML provider (e.g., Okta, Google Workspace):
- In
cdk/lib/api-stack.ts, find theCfnUserPoolIdentityProviderblock forEntraIDand update theproviderDetails(issuer URL, client ID/secret) andattributeMapping. - Update
frontend/src/functions/authService.js— change thecustomprovider name passed tosignInWithRedirectto match the new provider's name. - Update redirect/callback URIs in the identity provider's app registration to include your Amplify URL.
- Redeploy:
cdk deploy.
AWS docs:
- OIDC: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-oidc-idp.html
- SAML: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-saml-idp.html
- Add the Lambda handler in
cdk/lambda/handlers/<your-handler>.js(Node) or a new Python module. - Define a new
lambda.Functionincdk/lib/api-stack.ts. - Wire up the API Gateway route and authorizer in
api-stack.ts. - Update
OpenAPI_Swagger_Definition.yamlto reflect the new endpoint. - Run
cdk deploy.
Example (Node.js):
const myHandler = new lambda.Function(this, `${id}-MyHandler`, {
runtime: lambda.Runtime.NODEJS_22_X,
code: lambda.Code.fromAsset("lambda"),
handler: "handlers/myHandler.handler",
environment: {
SM_DB_CREDENTIALS: db.secretPathUser.secretName,
RDS_PROXY_ENDPOINT: db.rdsProxyEndpoint,
},
});
const myResource = this.api.root.addResource("my-feature");
myResource.addMethod("POST", new apigw.LambdaIntegration(myHandler), {
authorizer: this.adminAuthorizer,
});Existing handlers in cdk/lambda/handlers/ (e.g., adminHandler.js, chatSessionHandler.js) are good patterns to follow.
Frontend code is in frontend/src/:
pages/— route-level pagescomponents/— reusable UI blocksproviders/— React context (e.g.,UserSessionContext,ModeContext)
// frontend/src/pages/NewFeature/NewFeature.tsx
export default function NewFeature() {
return <div>New Feature page</div>;
}
// frontend/src/App.tsx
import NewFeature from "./pages/NewFeature/NewFeature";
<Route path="/new-feature" element={<NewFeature />} />Wrap with <ProtectedRoute> if admin-only.
Edit frontend/src/components/Footer.tsx:
<div className="text-sm text-muted-foreground">
© {new Date().getFullYear()} Your Organization Name.
</div>Or use a Vite env variable to avoid rebuilding for content changes:
© {new Date().getFullYear()} {import.meta.env.VITE_WEBSITE_NAME || 'Knowledge Bridge'}.The text generation Lambda uses Haiku for fast operations (query rewriting, intervention checks). The model ARN is stored as an SSM parameter:
/KBA/LLM/ModelArn
To update the model, change the SSM parameter value via the AWS Console or CLI:
aws ssm put-parameter \
--name "/KBA/LLM/ModelArn" \
--value "us.anthropic.claude-haiku-4-5-20251001-v1:0" \
--type String \
--overwriteRules when updating:
- The ARN must use the
us.cross-region inference prefix (e.g.,us.anthropic.claude-...). ModelArnshould be a fast, low-cost model suitable for query rewriting and lightweight checks.
Note: If you switch to a different model family, you'll also need to update the IAM policy on the
lambdaTextGenrole to allowbedrock:InvokeModelfor the new model ARN. This is configured incdk/lib/api-stack.tsundertextGenBedrockPolicyStatement.
For guardrail configuration, see Docs/BEDROCK_GUARDRAILS.md.
Migrations live in cdk/lambda/db_setup/migrations/ and follow a numbered naming convention.
// cdk/lambda/db_setup/migrations/015_add_new_feature_table.js
exports.up = async function (knex) {
return knex.schema.createTable('new_feature', (table) => {
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
table.string('name').notNullable();
table.text('description');
table.timestamps(true, true);
});
};
exports.down = async function (knex) {
return knex.schema.dropTable('new_feature');
};Token and message limits (daily token limit, max characters per message, min exchanges before suggestion, etc.) are admin-configurable at runtime — they're stored in the database and loaded by the Lambda on startup. No redeployment is needed to change them.
For details on how to configure these via the admin dashboard, see the User Guide.
The ingestion pipeline is an AWS Glue Python Shell job at cdk/glue/sharepoint_ingestion.py. Key areas to modify:
- SharePoint content source: The job uses the Microsoft Graph API (via
msgraph-sdk) to fetch list items. To change which SharePoint sites or lists are ingested, update the site/list IDs in the job or in thesites/site_sourcesdatabase tables. - Chunking logic: Text splitting happens inside the job before embedding. Adjust chunk size and overlap directly in the script.
- Embedding: Chunks are embedded by calling Amazon Bedrock (Cohere Embed English v3). To swap embedding models, update the model ID in the script and ensure the Glue IAM role has
bedrock:InvokeModelpermission for the new model ARN. - Schema changes: If new metadata columns are needed, add a migration in
cdk/lambda/db_setup/migrations/and redeployDBFlowStackbefore the next ingestion run.
To trigger a run manually, use the Run Ingestion button in the admin dashboard or call POST /admin/ingestion/trigger.
The current retrieval pipeline embeds the user query, runs a cosine similarity search against document_vectors, and passes the top-N chunks directly to the LLM. As the knowledge base grows to cover many topic areas, chunks that are semantically similar to the query but not actually relevant to the specific question can start slipping into that top-N. Adding a re-ranking step between retrieval and generation filters those out and can significantly improve answer quality.
When to consider this: When the number of ingested documents grows large enough that retrieval precision drops noticeably — i.e. the LLM is receiving off-topic chunks and hedging or hallucinating more often.
The only file that needs to change is cdk/lambda/textGeneration/helpers/bedrock.py, specifically the retrieve_documents function. No other files need modification — chat.py calls retrieve_documents() and receives the same return shape regardless.
The change is two steps:
- Fetch a larger candidate set from pgvector — increase the SQL
LIMITto e.g. 3× yourmax_context_chunksso the re-ranker has candidates to work with:
# Before
ORDER BY v.embedding <=> %s::vector
LIMIT %s
# ...
_vector_literal(embedding),
max_context_chunks,
# After — fetch a wider candidate pool
CANDIDATE_MULTIPLIER = 3
ORDER BY v.embedding <=> %s::vector
LIMIT %s
# ...
_vector_literal(embedding),
max_context_chunks * CANDIDATE_MULTIPLIER,- Call Bedrock Rerank and slice the top-N — add this after the pgvector query returns
results:
if results:
rerank_response = bedrock_client.rerank(
rerankingConfiguration={
"type": "BEDROCK_RERANKING_MODEL",
"bedrockRerankingConfiguration": {
"modelConfiguration": {
"modelArn": f"arn:aws:bedrock:{os.environ['AWS_REGION']}::foundation-model/amazon.rerank-v1:0"
},
"numberOfResults": max_context_chunks,
},
},
sources=[
{"type": "INLINE", "inlineDocumentSource": {"type": "TEXT", "textDocument": {"text": r["content"]}}}
for r in results
],
textSources=[{"type": "QUERY", "textQuery": {"text": query}}],
)
reranked_indices = [item["index"] for item in rerank_response["rerankingResults"]]
results = [results[i] for i in reranked_indices]IAM: The Lambda execution role needs bedrock:Rerank added to its Bedrock policy in cdk/lib/api-stack.ts.
Cost: Bedrock Rerank (amazon.rerank-v1:0) charges per 1,000 tokens reranked — small relative to the generation call, and available in ca-central-1.
CDK / Backend:
cd cdk
npm install
npm run build
cdk deployPython Lambda dependencies:
The textGeneration Lambda does not auto-install requirements.txt at deploy time — dependencies come from Lambda layers (psycopg2, etc.). If you add a new Python dependency, you'll need to either add a layer or set up a bundling step in CDK.
Glue job dependencies:
Edit cdk/glue/requirements.txt and redeploy the GlueStack. The CDK stack uploads the file to S3 and passes it to the Glue job as --additional-python-modules. See Docs/DEPENDENCY_MANAGEMENT.MD for details.
Frontend:
cd frontend
npm install
npm run build # production buildRun the dev server manually with npm run dev (Vite).
CI/CD: See Docs/DEPLOYMENT_GUIDE.md for pipeline specifics.
By default, all encrypted resources in this project (RDS, S3, Secrets Manager) use AWS managed keys. No configuration is required to use them, but they offer limited control over key rotation, auditing, and cross-account access.
If your organization requires customer-managed KMS keys (CMKs), see Docs/AWS_MANAGED_KEYS.md for a full list of affected resources and the exact CDK property changes needed in each stack.
- Lambda Timeout: Increase
timeoutin the CDK function definition. - Memory/Latency: Increase
memorySize; check VPC and RDS proxy config. - Database: Verify
SM_DB_CREDENTIALSin Secrets Manager and RDS proxy endpoint. - Cognito: Check user pool and client IDs in
frontend/.env. - Guardrails: See
Docs/BEDROCK_GUARDRAILS.mdfor topic policies, allow rules, and testing. - Logging: CloudWatch logs are the primary debugging source for all Lambdas.