Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

BRANCH

A non-profit accounting platform — projects, donors, donations, expenditures, reports, and user management. Built by Code 4 Community.

Nx-managed monorepo: a Next.js frontend, six AWS Lambda backend services, a Postgres database, and Terraform-managed infrastructure.

Stack

LayerWhat
FrontendNext.js 15 (App Router, static export), React 19, Chakra UI v3 + Tailwind v4
BackendSix Node 20 Lambdas (auth, donors, expenditures, projects, reports, users), TypeScript, Kysely
DatabasePostgreSQL 17 on RDS; SQL migrations run by CI before each deploy
AuthAWS Cognito (invitation-only, optional TOTP MFA); authorization policy in @branch/rbac
HostingS3 + CloudFront for the frontend, API Gateway + Lambda for the API
InfraTerraform 1.13, state in S3, secrets in Infisical

Quick start

Local development runs the backend under Docker Compose. You need Docker, Docker Compose, and Make.

cd apps/backend
cp .env.example .env # fill in the Cognito values, see below
make up # start postgres + all six services
make migrate # apply migrations, seed, regenerate types
make health # verify every service is answering

The frontend runs separately:

cd apps/frontend
npm ci
npm run dev

COGNITO_USER_POOL_ID and COGNITO_CLIENT_ID must be the real dev pool values — there is no local Cognito emulator. Get them with:

cd infrastructure/aws && terraform output cognito_user_pool_id cognito_client_id

First admin

branch.users.is_admin can only be set by an existing admin, so the first one in any environment is bootstrapped in SQL:

cd apps/backend && make grant-admin EMAIL=you@example.com

Accounts are invitation-only: a branch.users row with cognito_sub IS NULL is a pending invitation, and POST /auth/register claims it. A Cognito user created out of band has no matching row and will be rejected.

Documentation

AGENTS.md files are the source of truth, not README files. Start at the root AGENTS.md for the repo map and conventions, then:

DocCovers
apps/backend/AGENTS.mdServices, ports, auth model, env vars
apps/backend/db/README.mdMigration authoring rules and workflow
apps/frontend/AGENTS.mdFrontend patterns and build model
shared/rbac/README.mdThe role/permission matrix
infrastructure/AGENTS.mdTerraform root modules and apply flow
.github/AGENTS.mdCI/CD workflows and the PR review bot

apps/frontend/README.md is create-next-app boilerplate and does not describe this project.

Deployment

Nothing is deployed by hand. Merging to main triggers:

  • terraform-apply for infrastructure changes (gated on the production environment)
  • lambda-deploy — builds the bundles, snapshots RDS, applies migrations, then updates function code
  • frontend-deploy — builds the static export, syncs to S3, invalidates CloudFront

Pull requests get a terraform-plan comment, and adding the test-environment label spins up an ephemeral per-PR preview stack.

Contributing

Squash-merge only, one approval required, merge queue enabled. frontend-ci, lambda-tests, and terraform-plan-summary must pass. Prettier and ESLint run on push via husky.

Schema changes are always migrations — additive within a single PR, since migrations apply to production before the new code deploys.

About

Branch GitHub Admin

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages