From df8fedf69f9a3aa103b5206907ab73f3a609c585 Mon Sep 17 00:00:00 2001 From: Mahakisore7 Date: Sun, 20 Sep 2026 18:21:59 +0530 Subject: [PATCH] docs: add Mermaid architecture diagram, badges, and hackathon submission-form draft MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README: replaces the ASCII architecture diagram with a Mermaid flowchart (renders natively on GitHub), adds a badge row, a live-demo/video/docs quick-nav, and a table of contents. New docs/SUBMISSION-FORM.md: ready-to-paste draft answers for the AWS First Commit hackathon submission form (project description, AWS usage, team contributions, and genuine feedback on the AWS services used — Bedrock's account-gating friction, the Marketplace payment-instrument error, SES sandbox constraints — drawn from what was actually hit while building this). Video link and team-contribution fields left as TODOs since only the user can fill those in. Co-Authored-By: Claude Sonnet 5 --- README.md | 124 ++++++++++++++++++---- docs/SUBMISSION-FORM.md | 229 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 333 insertions(+), 20 deletions(-) create mode 100644 docs/SUBMISSION-FORM.md diff --git a/README.md b/README.md index 1febd40..3f8d1ac 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,25 @@ +
+ # StockSync +**Inventory that never loses a sale — even offline.** + An offline-first inventory sync engine for kirana shops running more than one billing counter on a single, often-unreliable internet connection. + +[![CI](https://github.com/Monolithic-Dev/stocksync/actions/workflows/ci.yml/badge.svg)](https://github.com/Monolithic-Dev/stocksync/actions) +[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) +![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white) +![AWS CDK](https://img.shields.io/badge/AWS%20CDK-FF9900?logo=amazonaws&logoColor=white) +![React](https://img.shields.io/badge/React-61DAFB?logo=react&logoColor=black) +![DynamoDB](https://img.shields.io/badge/DynamoDB-4053D6?logo=amazondynamodb&logoColor=white) + +[**Live demo**](https://main.d18ash44o1uc8d.amplifyapp.com) · [Demo video](#) · [Architecture](#architecture) · [Docs](docs/general/00-INDEX.md) + +
+ +--- + Two counters can sell the same item while both are offline, for any duration, in any order — and when they reconnect, StockSync guarantees the stock count reconciles to one mathematically correct, non-lossy state, @@ -10,11 +28,24 @@ with a minimal client, **StockSync Counter**, that exists to make the engine's correctness demonstrable on screen, not to be a sellable point-of-sale product. -- **Live demo:** https://main.d18ash44o1uc8d.amplifyapp.com - (sign up to create a shop, or sign in to an existing one — shop identity now comes from your - Cognito account, not a URL query param; open a second window signed into the same shop to see - the two-counter conflict scenario) -- **Demo video:** _TODO — add the recorded demo video link here._ +> **Live demo:** https://main.d18ash44o1uc8d.amplifyapp.com +> — sign up to create a shop, or sign in to an existing one (shop identity +> comes from your Cognito account, not a URL query param); open a second +> window signed into the same shop to see the two-counter conflict scenario. +> +> **Demo video:** _TODO — add the recorded demo video link here._ + +## Contents + +- [The problem, in one paragraph](#the-problem-in-one-paragraph) +- [Architecture](#architecture) +- [What makes this correct, not just working](#what-makes-this-correct-not-just-working) +- [Running it locally](#running-it-locally) +- [What we learned](#what-we-learned) +- [Project structure](#project-structure) +- [Scope](#scope) +- [Beyond the core sync engine — also shipped](#beyond-the-core-sync-engine--also-shipped) +- [Where this goes next](#where-this-goes-next) ## The problem, in one paragraph @@ -31,23 +62,76 @@ concurrent changes to the same shared number correctly, which is exactly what a CRDT is built for. See [`docs/general/01-PRD.md`](docs/general/01-PRD.md) §2 for the full problem statement. -## Architecture, summarized +## Architecture +```mermaid +flowchart TB + subgraph Client["StockSync Counter — React"] + UI["Offline queue (IndexedDB)"] + end + + subgraph Gateway["Amazon API Gateway"] + REST["REST API
Cognito JWT authorizer"] + WS["WebSocket API
$connect / $disconnect"] + end + + subgraph Compute["AWS Lambda"] + Intake["write-intake
idempotency check"] + Resolver["conflict-resolver
packages/core merge logic"] + Push["ws-push"] + Notify["notify-alerts"] + Auth["Cognito triggers
postConfirmation · staffInvite"] + end + + Queue["Amazon SQS FIFO
MessageGroupId = item_id"] + + subgraph Data["Amazon DynamoDB"] + Records[("inventory_records")] + Dedup[("write_dedup")] + Audit[("audit_log")] + Conns[("ws_connections")] + end + + Streams["DynamoDB Streams"] + Bedrock["Amazon Bedrock
conflict explanations · advisory only"] + SES["Amazon SES
low-stock / conflict emails"] + Cognito["Amazon Cognito
owner · manager · counter_staff"] + CW["Amazon CloudWatch
ConflictRate · IdempotencyHitRate"] + + UI -- "POST /transactions" --> REST + REST --> Intake + Intake -- "enqueue, ordered per item" --> Queue + Queue --> Resolver + Resolver -- "TransactWriteItems" --> Records + Resolver --> Dedup + Resolver --> Audit + Resolver -. "same-field conflict" .-> Bedrock + Records --> Streams + Streams --> Push + Streams --> Notify + Push -- "WSS live push" --> WS + WS --> UI + Notify --> SES + Cognito --> Auth + Auth -. "owner group on signup" .-> Records + REST -. verifies JWT .-> Cognito + Compute -. "ConflictRate / IdempotencyHitRate" .-> CW + + classDef aws fill:#FF9900,stroke:#232F3E,color:#232F3E,font-weight:bold; + classDef data fill:#4053D6,stroke:#232F3E,color:#fff,font-weight:bold; + classDef client fill:#61DAFB,stroke:#232F3E,color:#232F3E,font-weight:bold; + class REST,WS,Queue,Bedrock,SES,Cognito,CW aws; + class Records,Dedup,Audit,Conns data; + class UI client; ``` -StockSync Counter (React, offline queue via IndexedDB) - │ POST /transactions ▲ WSS live push - ▼ │ -API Gateway (REST + WebSocket) - │ - ▼ -Lambda: write-intake → SQS FIFO (MessageGroupId = item_id) → Lambda: conflict-resolver - │ idempotency check via write_dedup │ imports packages/core for all merge logic - │ │ TransactWriteItems: inventory_records + audit_log + write_dedup - │ │ Bedrock for same-field conflict explanations (non-blocking) - ▼ ▼ - Amazon DynamoDB - inventory_records / write_dedup / audit_log / ws_connections -``` + +The detail that matters most and is easiest to get wrong: **`MessageGroupId` +is the inventory item's ID, not the sending counter's ID.** Group by +counter instead, and two counters editing the same item race each other in +parallel Lambda invocations — which reintroduces the exact bug this +project exists to prevent. + +## What makes this correct, not just working Every one of these pieces is load-bearing, not decorative: diff --git a/docs/SUBMISSION-FORM.md b/docs/SUBMISSION-FORM.md new file mode 100644 index 0000000..3a41030 --- /dev/null +++ b/docs/SUBMISSION-FORM.md @@ -0,0 +1,229 @@ +# StockSync — Hackathon Submission Form (draft answers) + +Draft, ready-to-paste answers for the AWS First Commit hackathon submission +form. Review each field, edit anything that doesn't sound like you, fill in +the two fields only you can (video link, team contributions), then paste +into the actual form. + +--- + +## YouTube video demo link * + +_TODO — record and paste the link here. Must be published or unlisted, ≤3 +minutes, covering: about the project, tech stack and architecture, how you +used AWS, and learning/growth (optional). See +[`docs/general/10-DEMO-PLAN.md`](general/10-DEMO-PLAN.md) for the full +rehearsed script — it's built to hit all four points inside 3 minutes._ + +--- + +## What does your project do? * + +> What problem does your project solve, and who is it for? + +StockSync is an offline-first inventory sync engine for small Indian shops +("kirana stores") that run more than one billing counter on a single, +often-unreliable internet connection — a second register during rush hour, +or an owner checking stock from their own phone. + +When the connection drops and two counters sell the same item while both +are offline, most systems either block the sale or let whichever counter +reconnects last silently overwrite the other's numbers — erasing a real +sale from the record. Nobody notices until the stock count is wrong and a +customer is told an item is in stock when it isn't. For thin-margin retail, +that's a recurring, real cost, not an edge case. + +StockSync makes that mathematically impossible. Two counters can go +offline, sell the same item, restock it, and change its price — in any +order, for any duration — and when they reconnect, the stock count always +converges to one correct, non-lossy state. Genuine same-field conflicts +(e.g. two different prices set concurrently) are never silently guessed; +they're flagged for a human, with an AI-generated plain-language +explanation to help them decide fast. + +It's built for small multi-counter retailers in India today, but the +underlying problem — two offline devices sharing one number that must +reconcile correctly — applies to any offline-tolerant system, not just +point-of-sale. + +--- + +## How did you use AWS in your project? * + +> Build it: How have you used AWS open source stack (mention tools name) +> Ship it: How have you used AWS services (mention services name) + +**Build it — AWS open-source tooling:** +- **AWS CDK** (TypeScript) — the entire stack (DynamoDB tables, Lambdas, + API Gateway REST + WebSocket, Cognito, SQS, SES, CloudWatch) is defined + as code in `infra/cdk`, with unit tests asserting IAM policies never use + a wildcard resource (with one documented, unavoidable exception — + `dynamodb:ListStreams`, which AWS has no resource-level IAM scoping for + at all, confirmed by inspecting the synthesized CloudFormation template). +- **AWS SDK for JavaScript v3** (`@aws-sdk/*`) — every Lambda handler talks + to DynamoDB, SQS, SES, Cognito, Secrets Manager, and Bedrock through the + v3 modular clients. +- **aws-sdk-client-mock** — every AWS SDK call in the Lambda test suite is + mocked at the client level, not the network level, keeping ~150+ tests + fast and deterministic. + +**Ship it — AWS services in production:** +- **AWS Lambda** — every unit of compute: write intake, conflict + resolution, sync/audit queries, WebSocket connect/disconnect/push, + Cognito triggers (post-confirmation, staff invite), CRUD/checkout + handlers, and the SES notification consumer. +- **Amazon DynamoDB** — the system of record (`inventory_records`, + `write_dedup`, `audit_log`, `ws_connections`, plus the Tier-2 tables: + `products`, `categories`, `suppliers`, `orders`), with DynamoDB Streams + driving both real-time WebSocket push and the SES alert pipeline. +- **Amazon SQS (FIFO)** — orders every write touching a given inventory + item strictly by item ID (not by sending counter), which is the single + most load-bearing correctness guarantee in the whole system. +- **Amazon API Gateway** — both REST (write/sync/CRUD/checkout endpoints) + and WebSocket (live push to open counters), with a Cognito JWT authorizer + on every REST route. +- **Amazon Cognito** — real multi-tenant authentication: self-signup + creates a shop and its owner; owners invite manager/counter-staff + accounts; every request's `shop_id` comes from the verified JWT, never + a client-supplied value. +- **Amazon Bedrock** (Claude Haiku) — generates a plain-language + explanation of a genuine same-field conflict for the human resolving it + — strictly advisory, never the thing that decides the value. +- **Amazon SES** — low-stock and conflict-review email alerts to a shop's + owner, triggered off the same DynamoDB Stream that already powers + real-time sync. +- **Amazon CloudWatch** — custom `ConflictRate`/`IdempotencyHitRate` + metrics plus queue-depth dashboards. +- **AWS Secrets Manager** — holds the cross-account Bedrock credentials + (see the AWS feedback section below for why that workaround exists). +- **AWS Amplify Hosting** — serves the React client. + +--- + +## Blog links + +_Publish `docs/BLOG-POST.md` to the AWS Builder Center, then paste the +published link here. The draft is already written — "Two billing +counters, one bad connection, and the bug that erases a sale" — covering +the PN-Counter/vector-clock design decisions, the load-bearing +`MessageGroupId = item_id` detail, and a real concurrency bug an edge-case +hardening pass caught. Nothing left to do but publish it and paste the +link._ + +--- + +## Team leader's contributions * + +> Individual roles and key deliverables completed by team member. + +_TODO — edit this to describe your actual role. Draft below assumes a +solo build; adjust if that's not accurate:_ + +**Maha Kisore** — sole builder. Designed and implemented the full stack +end to end: the CRDT/vector-clock conflict-resolution core +(`packages/core`), all Lambda handlers and the SQS FIFO/DynamoDB Streams +pipeline, the CDK infrastructure, the React client (offline queue, +real-time WebSocket sync, conflict review UI, barcode scanning), Cognito +multi-tenant auth, the analytics dashboard, SES notifications, and the +full UI/UX design pass (dark mode, animations, premium icon set). Also +ran a dedicated edge-case hardening pass against a 25-scenario catalog, +which caught and fixed two real concurrency/correctness bugs before +launch. + +## Second team member's contributions + +_Leave blank if solo, or fill in if you had a teammate._ + +## Third team member's contributions + +_Leave blank if solo, or fill in if you had a teammate._ + +--- + +## Help us evaluate you: your feedback on the AWS services you used * + +> Tell us what you didn't like about the AWS services you used and what +> could be better, whether technical or onboarding related. + +**Bedrock's account-level model-access gating was the single biggest +friction point.** A brand-new AWS account can't invoke Bedrock models at +all until an "Anthropic use-case details" approval clears — and that +approval goes through a Support case with no guaranteed SLA on Basic +support, which is exactly the plan every new/personal AWS account starts +on. For a hackathon with a hard deadline, an unpredictable, un-appealable +wait on a **generative AI feature specifically** is a real barrier — +Bedrock is the one service most likely to be the differentiator in a +project like this, and it's also the one most likely to be blocked when +you need it most. A self-service "instant sandbox" tier for Bedrock, +similar to how most other services just work the moment you enable them, +would remove a lot of unnecessary hackathon friction. + +**AWS Marketplace's payment-instrument checks were opaque.** Separately, +provisioning a Bedrock model subscription through a second account failed +with `INVALID_PAYMENT_INSTRUMENT` even with a valid card on file as the +account default — it turned out the account's *default payment method* +was set to UPI AutoPay, which Marketplace subscriptions silently reject in +favor of a credit/debit card, with no explicit error message pointing at +that as the cause. Discovering the actual root cause took directly testing +the API call and reading the raw exception text, not anything AWS's UI +surfaced proactively. + +**SES sandbox mode's requirement that sender *and every recipient* be +individually verified** makes sense for abuse prevention, but for a +demo/hackathon context it means the "real" version of a notification +feature (alerting an arbitrary shop owner's real email) isn't actually +testable without a production-access request — so we could only verify +the mechanism works, not deliver a fully realistic demo of it. + +**Amplify Hosting's CLI deploy flow (no GitHub App connected) requires +manually scripting** `create-deployment` → zip → presigned-URL `curl` → +`start-deployment` → poll — it works, but it's clearly the secondary path +next to the git-connected auto-deploy flow, and a couple of the CLI +commands (e.g. `create-deployment` rejecting a second call while a prior +job is still `PENDING`) produce cryptic-enough errors that recovering +requires cross-referencing `get-job` state manually rather than anything +self-explanatory. + +--- + +## What did you like about the AWS services you used? * + +> Tell us what worked well for you, whether that was a specific service, +> the docs, the setup, or anything that made building easier. + +**CDK made "infrastructure as code" genuinely load-bearing, not just a +buzzword.** Being able to write a unit test that asserts *no IAM policy in +the synthesized template uses a wildcard resource* — and have it actually +catch a real over-broad grant during development — is a level of +confidence a console-clicked stack can't give you. The one time the test +needed an exception (`dynamodb:ListStreams`, which AWS itself has no +resource-level IAM scoping for), CDK's synthesized template made that +completely verifiable rather than a guess. + +**DynamoDB Streams + `TransactWriteItems`** were a genuinely elegant fit +for this problem: Streams became a free, already-running event source we +reused for real-time WebSocket push *and* email alerts, without adding any +new write-path code, and `TransactWriteItems` meant a record update, its +audit-log entry, and its idempotency claim could never drift out of sync +with each other even under a crash mid-write. + +**Cognito's built-in triggers (post-confirmation, custom user creation)** +made "self-signup creates a shop and makes you its owner" a clean, +declarative flow instead of a custom auth service — and the JWT authorizer +on API Gateway meant every route got tenant isolation for free, without +hand-rolling token verification in every handler. + +**The AWS SDK v3's modular clients paired extremely well with +`aws-sdk-client-mock`** — every Lambda handler's AWS calls could be +unit-tested in milliseconds against a mocked client, while the +correctness-critical DynamoDB paths were additionally tested against a +real local DynamoDB instance (`dynamodb-local`) for true integration +coverage. That two-tier testing setup, entirely enabled by how the v3 SDK +is structured, is a big part of why this project has ~200+ tests across +the stack. + +**Overall, the actual serverless services (Lambda, DynamoDB, SQS, API +Gateway, Cognito) just worked** — provisioned cleanly through CDK, no +console clicking required, and behaved exactly as documented. The friction +in this build was almost entirely concentrated in Bedrock/Marketplace +account-gating, not in the core services the architecture is built on.