Internal Slack DM assistant for Sea Haven Industries, powered by AWS Bedrock
The Bedrock Agents service fetches the guardrail config via GetGuardrail before applying it. The role only had ApplyGuardrail, so every agent invocation logged an AccessDenied and tripped the CIS 4.1 UnauthorizedAPICalls alarm. Scoped to the same guardrail ARNs already granted for ApplyGuardrail. Cross-reviewed (IAM change): APPROVE, no findings. |
||
|---|---|---|
| .github | ||
| bin | ||
| lambda | ||
| lib | ||
| scripts | ||
| services/socket-mode | ||
| .gitignore | ||
| cdk.context.json | ||
| cdk.json | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| TODO.txt | ||
| tsconfig.json | ||
Seahaven Slack Bot — Alex
Internal Slack assistant for Sea Haven Industries, powered by AWS Bedrock. Employees can DM Alex or @mention Alex in any channel to ask about vendors, payments, work orders, purchase orders, Amazon sites, company policies, and more.
Architecture
Slack (WebSocket)
│
▼
ECS Fargate (Socket Mode) ← persistent WebSocket connection to Slack
│
├── DM (message.im) ──────────────────┐
├── @mention (app_mention) ────────────┤
│ async invoke
│ ▼
│ slack-processor Lambda ← calls Bedrock Agent, logs to DynamoDB, posts reply
│ │
│ ▼
│ Bedrock Agent — Alex (Claude Sonnet 4.5)
│ ├── Knowledge Base (AOSS + S3)
│ ├── QBO_Lookup action group ← QuickBooks vendor search (VPC, static IP)
│ ├── Google_Maps_Lookup action group ← fallback vendor search
│ └── WO_PO_Lookup action group ← WO, PO, site, and payment lookups (DynamoDB direct)
│
└── App Home opened ──────────────────→ app-home Lambda ← publishes Block Kit capabilities view
API Gateway (bot.seahaven.com)
│
└── GET /qbo/*
▼
qbo-oauth Lambda (VPC, static IP) ← OAuth 2.0 connect/callback/disconnect/launch
Outbound IP: 52.202.83.13 (NAT Gateway in seahaven-vpc)
EventBridge (daily 02:00 UTC)
│
├── notion-sync Lambda ← exports Office Operations Notion pages → S3 → KB ingestion
├── po-sync Lambda ← exports purchase-orders DynamoDB → S3 → KB ingestion
└── workorder-sync Lambda ← exports WorkOrders + Comments DynamoDB → S3 → KB ingestion
What Alex Can Do
- Vendor Lookup — search QBO for existing vendors, KB for approved lists, Google Maps for new options
- Payment & Invoice Status — check if a vendor has been paid, if an invoice is scheduled, look up by check number
- Work Order Lookups — status, assignments, comments, and full history by WO number
- Purchase Order Lookups — status, line items, supplier, and ship-to details by PO number
- Site Lookups — Amazon facility address and details by site code, or list all sites in a state
- Company Policies & SOPs — employee handbook, SA8000 compliance, and operational procedures
Vendor Query Priority
- QBO — existing vetted vendors in QuickBooks Online
- Knowledge Base — approved vendor lists in internal docs
- Google Maps — fallback for net-new vendor discovery
Stack
| Resource | Details |
|---|---|
| AWS Account | 328440206208, us-east-1 |
| CDK | TypeScript, v2 |
| LLM | Claude Sonnet 4.5 (anthropic.claude-sonnet-4-5-20250929-v1:0) |
| Embeddings | Amazon Titan Embed Text V2 (1024 dimensions) |
| Vector Store | OpenSearch Serverless (VECTORSEARCH) |
| Socket Mode | ECS Fargate (seahaven-socket-mode) — persistent WebSocket to Slack |
| Conversation Log | DynamoDB seahaven-conversations (90-day TTL) |
| Unanswered Questions | DynamoDB seahaven-unanswered-questions (180-day TTL) |
| Payment Data | DynamoDB PaymentsDashboard (via payments-dashboard) |
| PO Data Source | DynamoDB purchase-orders (via po-ingest) |
| Work Order Data Source | DynamoDB WorkOrders + WorkOrderComments (via workorder-ingest) |
| Site Assignments | DynamoDB verified-sites (auto-populated via po-ingest Streams pipeline) |
| VPC | seahaven-vpc (vpc-0d3d4b67bd0cf8a68) — QBO Lambdas + Socket Mode |
| Static Outbound IP | 52.202.83.13 (NAT Gateway for Intuit IP allowlist) |
| QBO OAuth URLs | bot.seahaven.com/qbo/connect, /qbo/callback, /qbo/disconnect, /qbo/launch |
Prerequisites
- Node.js 22+
- AWS CDK (
npm install -g aws-cdk) - Docker Desktop (required for Lambda bundling and Socket Mode container build)
- AWS credentials configured locally
Secrets Manager
These secrets must exist before deploying. The Slack, QBO, Maps, and app-level token secrets must be created manually before first deploy. The Notion secret is created automatically by CDK with a placeholder.
| Secret Name | Created | Structure |
|---|---|---|
seahaven/slack/credentials |
Manual (pre-deploy) | { "botToken": "xoxb-...", "signingSecret": "..." } |
seahaven/slack/app-level-token |
Manual (pre-deploy) | Plaintext xapp-... token with connections:write scope |
seahaven/qbo/oauth |
Manual (pre-deploy) | { "clientId": "", "clientSecret": "", "refreshToken": "", "realmId": "" } |
seahaven/google/maps-api-key |
Manual (pre-deploy) | { "apiKey": "" } |
seahaven/notion/api-key |
Auto (CDK) | { "apiKey": "secret_..." } |
Deployment
npm install
npx cdk bootstrap aws://328440206208/us-east-1 # first time only
npx cdk deploy
The deploy takes ~15 minutes on first run. The AOSS collection, vector index creation, and Docker image build are the slow steps.
Stack outputs after deploy:
KBDocsBucketName— S3 bucket to upload knowledge base documentsAgentId— Bedrock Agent ID (Alex)
Post-Deployment Setup
1. Upload knowledge base documents
Upload SA8000 compliance docs, SOPs, and the employee handbook to the S3 bucket printed in stack outputs.
aws s3 cp ./your-docs/ s3://<KBDocsBucketName>/ --recursive
2. Trigger KB sync
aws bedrock-agent start-ingestion-job \
--knowledge-base-id <from Bedrock console> \
--data-source-id <DataSourceId> \
--region us-east-1
3. Configure Notion sync
- Create a Notion internal integration at Settings → Connections → Develop or manage integrations
- Name it
seahaven-office-opsand associate it with the Office Operations teamspace - Copy the integration token and update: Secrets Manager →
seahaven/notion/api-key→ Edit
4. Configure Slack app
In the Slack API dashboard:
- Basic Information → update Display Name to "Alex", upload avatar
- Socket Mode → enable, generate app-level token with
connections:writescope - Store the app-level token in Secrets Manager as
seahaven/slack/app-level-token - Event Subscriptions → enable (no Request URL needed with Socket Mode)
- Subscribe to bot events:
message.im,app_mention,app_home_opened - OAuth & Permissions → Bot Token Scopes:
chat:write,im:history,app_mentions:read,channels:history,groups:history - App Home → enable Home Tab and Messages Tab
- Reinstall app to workspace
- Invite Alex to channels:
/invite @Alex
Project Structure
bin/
seahaven-slack-bot.ts CDK app entry point
lib/
seahaven-slack-bot-stack.ts Main stack
constructs/
knowledge-base.ts Bedrock KB + AOSS + S3 (via @cdklabs L2 construct)
bedrock-agent.ts Alex — Bedrock Agent + QBO/Maps/WO-PO-Site-Payment action groups
slack-handler.ts Processor Lambda + App Home Lambda + QBO OAuth + API Gateway
socket-mode.ts ECS Fargate service running Slack Socket Mode client
conversation-log.ts DynamoDB conversation + unanswered questions tables
notion-sync.ts EventBridge daily cron + notion-sync Lambda
po-sync.ts EventBridge daily cron + po-sync Lambda
workorder-sync.ts EventBridge daily cron + workorder-sync Lambda
services/
socket-mode/ Socket Mode ECS service (Dockerfile, TypeScript, @slack/socket-mode)
lambda/
slack-processor/ Calls agent, writes DynamoDB, posts Slack reply
app-home/ Publishes Block Kit capabilities view to App Home tab
qbo-oauth/ OAuth 2.0 connect/callback/disconnect/launch for QuickBooks
qbo-lookup/ Bedrock action group — QuickBooks vendor search
maps-lookup/ Bedrock action group — Google Maps Places search
wo-po-lookup/ Bedrock action group — WO, PO, site, and payment lookups (DynamoDB direct)
notion-sync/ Daily Notion → S3 export, triggers KB ingestion
po-sync/ Daily purchase-orders DynamoDB → S3 export, triggers KB ingestion
workorder-sync/ Daily WorkOrders DynamoDB → S3 export, triggers KB ingestion
scripts/
create-aoss-index.ts Manual fallback for AOSS index creation (not needed in normal deploy)
Known Maintenance Items
- QBO OAuth — the refresh token auto-rotates on every API call (persisted back to Secrets Manager). If the token ever expires (100 days of inactivity), reconnect via
https://bot.seahaven.com/qbo/connect. - Notion sync runs daily at 02:00 UTC automatically. Manual trigger: invoke
seahaven-notion-sync. - PO sync runs daily at 02:00 UTC. Scans
purchase-ordersDynamoDB table (from po-ingest). Manual trigger: invokeseahaven-po-sync. - Work order sync runs daily at 02:00 UTC. Scans
WorkOrdersandWorkOrderComments(from workorder-ingest). Manual trigger: invokeseahaven-workorder-sync. - Site assignments are auto-populated from the
verified-sitesDynamoDB table, maintained by the po-ingest DynamoDB Streams pipeline. No manual seeding required. - Unanswered questions are logged to
seahaven-unanswered-questionswhen Alex detects it couldn't answer a question. Review via DynamoDB console — records havestatus: "pending"and 180-day TTL. - Socket Mode — the ECS Fargate task maintains a persistent WebSocket connection to Slack. Monitor via CloudWatch Logs (
socket-modelog stream). The service auto-restarts on failure.