This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
seahaven-slack-bot/README.md
Adam Moussa 7607eb9e59
docs: add decommission tombstone to README (#101)
Stack seahaven-slack-bot torn down 2026-07-23; repo archived read-only.
Successor: sh-mcp.
2026-07-23 14:43:11 -04:00

15 KiB

Seahaven Slack Bot — Alex

☠️ DECOMMISSIONED 2026-07-23

This stack and the Alex Slack app were fully torn down on 2026-07-23 and this repository is archived read-only. The CloudFormation stack seahaven-slack-bot (328440206208/us-east-1), the Bedrock agent/KB/AOSS collection, the bot.seahaven.com endpoint, and the Slack app were deleted. Conversation-table exports and KB documents were backed up to ~/Desktop/seahaven-slack-bot-decommission-2026-07-23/ on Adam's workstation. The vendor secrets (seahaven/slack/*, seahaven/qbo/oauth, seahaven/google/maps-api-key, seahaven/notion/api-key) were retained in Secrets Manager for reuse.

Successor: the Sea Haven MCP platform (Sea-Haven-Industries/sh-mcp) — see its docs/design.md. Everything below describes the architecture as it existed at decommission.

TypeScript AWS CDK Slack CI

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
  • Company Policies & SOPs — employee handbook, SA8000 compliance, and operational procedures

Vendor Query Priority

  1. QBO — existing vetted vendors in QuickBooks Online
  2. Knowledge Base — approved vendor lists in internal docs
  3. 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 (read-only; owned by procurement-ingest / 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

Shared Resources

This bot reads the purchase-orders DynamoDB table read-only (via the po-sync and wo-po-lookup Lambdas, both granted grantReadData). The table is owned by the procurement-ingest repo (po-ingest stack), which is the sole authoritative writer. Any change to the purchase-orders schema must be coordinated with procurement-ingest (owner) and payments-dashboard (the other read-only consumer).

Cross-stack data contracts

This bot does not own any DynamoDB tables. It imports five tables owned by other stacks via Table.fromTableName(...) (lib/constructs/bedrock-agent.ts, workorder-sync.ts, po-sync.ts) and reads them read-only. Because the tables are imported by name, this stack has no compile-time link to the owner: an owner-side change to a table's name, key schema, attribute names, GSIs, encryption key, or lifecycle policy will silently break the Bedrock agent (Alex) at runtime, not at deploy. The CMK grant-gap incident (INFRA-95 / M-3) is precedent: because grantReadData on an imported table does not carry KMS permissions, every read failed with kms:Decrypt AccessDenied until an explicit grant was added.

Owner-side changes to any of these tables must be coordinated with this repo before they ship. Treat them as cross-repo migrations, not local edits.

Table Owner repo / stack Keys the bot depends on Attributes the bot reads Encryption
WorkOrders procurement-ingest / WorkorderIngestStack (cdk/wo_stack.py) PK work_order_id (S). GetItem by id; full-table Scan in workorder-sync. work_order_id, description, wo_status, customer, site_code, building, address, severity, priority, assigned_to, date_reported, scheduled_start, due_date, updated_at Shared CMK (/seahaven/dynamodb/cmk-arn)
WorkOrderComments procurement-ingest / WorkorderIngestStack (cdk/wo_stack.py) PK work_order_id (S), SK comment_id (S). Query by work_order_id. work_order_id, created_at (used for sort), commenter, text Shared CMK (/seahaven/dynamodb/cmk-arn)
purchase-orders procurement-ingest / po-ingest (cdk/po_stack.py) PK po_number (S). GetItem by number; full-table Scan in po-sync. po_number, po_status, email_type, cancelled_at, total_amount, currency, supplier.name, source_system, order_date, revision_date, payment_terms, requisition_number, department, submitted_by, on_behalf_of, ship_to.{name,address,location_code,attn}, line_items[].{description,amount,currency,need_by,category} Shared CMK (/seahaven/dynamodb/cmk-arn)
verified-sites procurement-ingest / po-ingest (cdk/po_stack.py) PK siteCode (S). GetItem by code only. siteCode, address, fullAddress, city, state, zip, latitude, longitude, notes AWS-managed (NOT CMK)
PaymentsDashboard payments-dashboard / payments-dashboard (template.yaml) PK pk (S), format payment#<check_number>. GetItem by pk; full-table Scan filtered begins_with(pk, "payment#"). pk, check_number, payee, amount_usd, method, status, send_payment_on, clear_status, cleared_date, invoice_numbers, company_subsidiary, bank_reference CMK (shared)

Resolved contract break (verified-sites by-state GSI, INFRA-180): the owner removed the by-state GSI on 2026-06-03 (procurement-ingest audit M-20, "0 reads in 30d"), which broke the bot's state-based site listings at runtime. Per the owner's decision the state-listing path was removed from the bot entirely (query code, lookup_site state parameter, and agent instructions) rather than restoring the GSI. lookup_site now supports siteCode point lookups only.

Documentation

The canonical map of Sea Haven's AWS infrastructure lives in Confluence. This project's seahaven-slack-bot stack is represented there as a Mermaid subgraph.

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 documents
  • AgentId — 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

  1. Create a Notion internal integration at Settings → Connections → Develop or manage integrations
  2. Name it seahaven-office-ops and associate it with the Office Operations teamspace
  3. Copy the integration token and update: Secrets Manager → seahaven/notion/api-key → Edit

4. Configure Slack app

In the Slack API dashboard:

  1. Basic Information → update Display Name to "Alex", upload avatar
  2. Socket Mode → enable, generate app-level token with connections:write scope
  3. Store the app-level token in Secrets Manager as seahaven/slack/app-level-token
  4. Event Subscriptions → enable (no Request URL needed with Socket Mode)
  5. Subscribe to bot events: message.im, app_mention, app_home_opened
  6. OAuth & Permissions → Bot Token Scopes: chat:write, im:history, app_mentions:read, channels:history, groups:history
  7. App Home → enable Home Tab and Messages Tab
  8. Reinstall app to workspace
  9. 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-orders DynamoDB table (from po-ingest). Manual trigger: invoke seahaven-po-sync.
  • Work order sync runs daily at 02:00 UTC. Scans WorkOrders and WorkOrderComments (from workorder-ingest). Manual trigger: invoke seahaven-workorder-sync.
  • Site assignments are auto-populated from the verified-sites DynamoDB table, maintained by the po-ingest DynamoDB Streams pipeline. No manual seeding required.
  • Unanswered questions are logged to seahaven-unanswered-questions when Alex detects it couldn't answer a question. Review via DynamoDB console — records have status: "pending" and 180-day TTL.
  • Socket Mode — the ECS Fargate task maintains a persistent WebSocket connection to Slack. Monitor via CloudWatch Logs (socket-mode log stream). The service auto-restarts on failure.