Synvya's business-facing web client for onboarding, profile management, and Square catalog publishing. The repo contains the Vite/React frontend, supporting Lambda functions, and deployment plumbing for Synvya's second-generation client site (https://account.synvya.com).
client/– Vite + React application (TypeScript, Tailwind) and local dev tooling.infra/– AWS SAM template, Lambda sources, and zipped artifacts for console-based deployments.internal/– Deployment references (GitHub secrets, IAM policy history, NIP-99 notes).docs/– Documentation for reservation messaging protocol and AI integration.
- Merchant keypair generation and secure storage (IndexedDB + AES-GCM encryption)
- NIP-07 window.nostr shim for signing events
- Kind 0 business profile publishing with business type tags
- NIP-96/NIP-98 media uploads via nostr.build
- 4-Message Protocol: Complete reservation negotiation flow
- Kind 9901: Initial reservation request
- Kind 9902: Reservation response (confirmed/declined/cancelled)
- Kind 9903: Reservation modification request
- Kind 9904: Reservation modification response
- Message Exchange: Encrypted NIP-59 gift-wrapped messages with NIP-17 Self CC pattern
- AI Integration: AI agents can discover and communicate with restaurants using standard Nostr protocols
- See NIP-RR repository for the complete protocol specification
- OAuth flow for connecting Square merchant accounts
- Catalog sync from Square's Catalog API (items, variations, categories, pricing)
- Nostr marketplace event generation:
- Kind 30402: Product listings for individual menu items with pricing, descriptions, and images
- Kind 30405: Product collections representing menu hierarchy (e.g., "Lunch Menu", "Appetizers Menu Section")
- Menu hierarchy support:
- Only
MENU_CATEGORYtypes create collections (notREGULAR_CATEGORY) - Top-level menus labeled as "Menu" (e.g., "Dinner Menu")
- Sub-level menu groups labeled as "Menu Section" (e.g., "Entrees Menu Section")
- Products reference both direct menu section and parent menu via
atags
- Only
- Preview mode: Test event generation without publishing to relays
- Lambda endpoints:
/square/catalog/:npub,/square/catalog/:npub/preview,/square/catalog/:npub/publish - Follows Gamma Markets Spec for product listings and collections
- Node.js 20+
- From
client/:npm install cp .env.example .env # Fill in the values described below npm run dev - The dev server runs on
http://localhost:5173.
| Variable | Purpose |
|---|---|
VITE_UPLOAD_NSEC |
nostr.build upload secret for photo storage |
VITE_UPLOAD_PROXY_URL |
Base URL that exposes /media/upload (CloudFront routes to the Lambda proxy). |
VITE_API_BASE_URL |
Base URL for the API Gateway that fronts the Lambda functions. |
VITE_DEFAULT_RELAYS |
Comma-separated relay list for publishing profile events. |
VITE_SQUARE_ENV |
sandbox or production |
VITE_SQUARE_APPLICATION_ID |
sandbox-sq****-********************** |
VITE_SQUARE_REDIRECT_URI` |
http://localhost:5173/square/callback |
- Production builds are generated with
npm run buildfromclient/which createsclient/dist/. .github/workflows/deploy.ymlautomates deployment on pushes tomainor manual dispatch:- Install deps (
npm ci), run the production build. - Assume IAM role
arn:aws:iam::122610503853:role/SynvyaClientGithubActions. aws s3 sync dist/ s3://client2-synvya-com --delete.- Optionally invalidate CloudFront distribution
E10XBQFR9NDURM.
- Install deps (
- GitHub repository secrets drive the workflow:
| Secret | Example Value |
|---|---|
AWS_REGION |
us-east-1 |
AWS_ROLE_ARN |
arn:aws:iam::123456789012:role/SynvyaClientGithubActions |
CLOUDFRONT_DISTRIBUTION_ID |
E1234567890ABC |
VITE_UPLOAD_PROXY_URL |
https://account.synvya.com |
VITE_API_BASE_URL |
https://abc123.execute-api.us-east-1.amazonaws.com |
VITE_SQUARE_ENV |
sandbox |
VITE_SQUARE_APPLICATION_ID |
sandbox-sq0idb-xxxxxxxxxxxxxxxxxxxx |
VITE_SQUARE_REDIRECT_URI |
https://account.synvya.com/square/callback |
The static hosting stack was configured directly via the AWS console:
- S3 bucket
client2-synvya-com- Region:
us-east-1. - Block Public Access disabled (serves via CloudFront), but bucket policy restricted to CloudFront Origin Access Control (OAC).
- Versioning left disabled (deploy pipeline uses
--deletesync).
- Region:
- CloudFront distribution pointing at the S3 bucket
- Origin Access Control connected to the bucket.
- Alternate domain name
account.synvya.comwith an ACM certificate inus-east-1. - Default root object
index.html, error responses mapped for SPA routing (404 → 200). - Additional behavior:
/media/*(and other API paths such as/square/*) forward to the API Gateway domain created below.
- Route 53 record –
account.synvya.comALIAS → CloudFront distribution.
The API Gateway, Lambda functions, and DynamoDB table were provisioned once (manually) and now run as managed infrastructure.
-
Resources in service
- HTTP API Gateway forwarding
/media/*to the upload proxy lambda and/square/*to the Square integration lambda. - DynamoDB table that stores Square OAuth connections.
- Secrets Manager entries holding the upload and Nostr signing keys.
- HTTP API Gateway forwarding
-
Updating Lambda code
- Follow the steps in Working on the Square Integration lambda locally (or the analogous upload proxy instructions) to build a zip containing
square.js,media.js,package*.json, andnode_modulesat the archive root. - Upload the zip through the Lambda console. No CloudFormation stack update is required for code-only changes.
- Follow the steps in Working on the Square Integration lambda locally (or the analogous upload proxy instructions) to build a zip containing
-
Reprovisioning (only if the infrastructure must be rebuilt)
infra/template.yamlcaptures the original SAM stack used to create the API, Lambda functions, and DynamoDB table. If the stack ever needs to be recreated from scratch, deploy that template via CloudFormation and supply fresh parameter values (UploadSecretArn,AllowedOrigins,SquareEnvironment,SquareApplicationId,SquareClientSecret,SquareRedirectUri,NostrRelays,NostrNsec).- After the stack is recreated, update
VITE_API_BASE_URL, CloudFront behaviors, and any environment variables/secrets to point at the new resources.
- AWS Secrets Manager
- Created secret (e.g.,
synvya-upload-nsec) inus-east-1. - Secret value stored as JSON:
{ "synvya-nsec": "nsec1..." }to match the Lambda default key. - Recorded the ARN for the SAM parameter
UploadSecretArn.
- Created secret (e.g.,
- Nostr Signing Key
- Separate secret storing the backend signing
nsecused by the Square Lambda (referenced via parameterNostrNsec).
- Separate secret storing the backend signing
- Square Developer Portal
- Created an application in the Square dashboard (production mode).
- Added redirect URI
https://account.synvya.com/square/callbackand sandbox equivalent for testing. - Captured Application ID and Client Secret for both SAM template parameters and local
.env.
- Configured an IAM OIDC provider for
token.actions.githubusercontent.com(if not already present). - Created role
SynvyaClientGithubActionswith trust policy limiting access to theSynvya/synvya-client-2repo. - Attached deployment policy allowing:
- S3 sync to
client2-synvya-com. - CloudFront invalidations on distribution
E10XBQFR9NDURM. - CloudFormation/Lambda access for manual updates when required.
- S3 sync to
- Stored the role ARN in GitHub secrets and validated
aws sts get-caller-identityfrom a workflow run.
- Lambda functions in production
synvya-upload-proxy– the NIP-98 upload proxy invoked by/media/upload.synvya-square-integration– handles Square OAuth, catalogue sync, and classified listing generation.
- Zipped artifacts
infra/lambda.zip(upload proxy) andinfra/synvya-square-integration.zipmirror the versions currently deployed. They can be re-uploaded via the Lambda console when an urgent hotfix is required without going through CloudFormation.
-
Install dependencies
cd infra/lambda npm install -
Manual packaging – create the ZIP that Lambda expects. Files must live at the root (no
lambda/folder):cd infra/lambda npm install --production zip -qr ../synvya-square-integration.zip \ square.js media.js package.json package-lock.json node_modules -
Upload – in the AWS console open
synvya-square-integration, choose Code → Upload from → .zip file, and selectinfra/synvya-square-integration.zip. -
Environment variables
| Variable | Example Value | Notes |
|---|---|---|
CORS_ALLOW_ORIGIN |
https://account.synvya.com |
Must include the frontend origin so fetches succeed. |
NOSTR_RELAYS |
wss://relay.damus.io,wss://nos.lol |
Relays queried for the merchant's kind‑0 profile. |
SQUARE_ENV |
sandbox |
sandbox or production. |
SQUARE_APPLICATION_ID |
sandbox-sq0idb-... |
Copied from Square developer portal. |
SQUARE_REDIRECT_URI |
https://account.synvya.com/square/callback |
Must match the Square app configuration. |
SQUARE_CONNECTIONS_TABLE |
SynvyaSquareConnections |
DynamoDB table name created by the SAM template. |
SQUARE_PRIMARY_KEY |
npub |
DynamoDB partition key (defaults to npub). |
SQUARE_VERSION |
2025-01-23 |
Square API version header. |
- Local testing:
npm run dev,npm run build,npm run preview. - Deployment: merge to
mainor trigger the “Deploy Frontend” GitHub Action. - Cache busting: workflow triggers CloudFront invalidation; re-run if assets appear stale.
# On the feature branch
npm test -- --run # All tests pass
npm run build # Build succeeds
git checkout main # Switch to main
git pull # Get latest
git checkout feature-branch
git merge main # Merge main into feature
npm test -- --run # Test again after merge