@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
# Stateless Design — Architecture Expertise Module
|
|
2
|
+
|
|
3
|
+
> Stateless design means each request contains all information needed to process it — servers don't store client state between requests. This enables horizontal scaling (any server can handle any request), simplifies deployment, and improves reliability. But not everything can or should be stateless — databases, WebSocket servers, and caches are inherently stateful. The goal is not to eliminate state, but to externalize it from the application process.
|
|
4
|
+
|
|
5
|
+
> **Category:** Scaling
|
|
6
|
+
> **Complexity:** Moderate
|
|
7
|
+
> **Applies when:** Designing services that need to scale horizontally, survive server failures, or deploy without downtime
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What This Is (and What It Isn't)
|
|
12
|
+
|
|
13
|
+
A stateless service treats every request as an independent, self-contained transaction. The server holds no memory of previous interactions — no session objects in memory, no user-specific data cached in the process, no sticky connections required. Every piece of information the server needs to handle a request arrives with the request itself: in headers, tokens, query parameters, or the request body.
|
|
14
|
+
|
|
15
|
+
The concept traces directly to HTTP's original design. HTTP/1.0 was stateless by specification — each request-response pair was independent. But the web quickly needed sessions (shopping carts, login state, multi-step forms), and developers bolted state onto a stateless protocol using cookies and server-side session stores. The modern "stateless design" movement is a return to the original principle, aided by better tools for externalizing state.
|
|
16
|
+
|
|
17
|
+
The Twelve-Factor App methodology codified this as Factor VI: "Execute the app as one or more stateless processes." The memory space or filesystem of the process can be used as a brief, single-transaction cache, but a twelve-factor app never assumes that anything cached in memory or on disk will be available on a future request. Any data that needs to persist must be stored in a stateful backing service, typically a database or external cache.
|
|
18
|
+
|
|
19
|
+
**The critical distinction: stateless does not mean "no state."** State absolutely exists — user sessions, shopping carts, authentication context, application configuration. The question is where that state lives:
|
|
20
|
+
|
|
21
|
+
| Approach | State location | Scalability | Failure impact | Complexity |
|
|
22
|
+
|---|---|---|---|---|
|
|
23
|
+
| Stateful server | In-process memory | Vertical only (sticky sessions) | Server death = lost sessions | Low initially, high at scale |
|
|
24
|
+
| Stateless + external store | Redis, database, or client token | Horizontal (any server, any request) | Server death = zero impact | Moderate initially, low at scale |
|
|
25
|
+
| Fully client-side | JWT, cookies, local storage | Horizontal (zero server state) | Server death = zero impact | Low initially, moderate at scale |
|
|
26
|
+
|
|
27
|
+
**What stateless design IS:**
|
|
28
|
+
|
|
29
|
+
- **Externalizing session state.** User sessions move from in-process memory to an external store (Redis, database) or to the client (JWT, encrypted cookies). The application process becomes disposable — kill it, restart it, replace it, scale it — without losing any user context.
|
|
30
|
+
- **Request self-sufficiency.** Each request carries its own authentication context (bearer token, API key), its own routing information, and enough data for the server to process it without looking up "who was this user last time."
|
|
31
|
+
- **Process disposability.** Any process can be killed at any time without data loss or user impact. New processes can start and immediately handle traffic. This directly enables zero-downtime deployments, autoscaling, and failure recovery.
|
|
32
|
+
|
|
33
|
+
**What stateless design IS NOT:**
|
|
34
|
+
|
|
35
|
+
- **Not "no database."** Stateless services still read from and write to databases. The database is stateful — the application process is not. The process is a pure function over the request plus external state.
|
|
36
|
+
- **Not "no caching."** Stateless services absolutely use caches. The distinction is between in-process caches (ephemeral, lost on restart, local to one instance) and external caches (Redis, Memcached — shared, persistent, accessible by all instances). Both are valid; the in-process cache just cannot be relied upon across requests.
|
|
37
|
+
- **Not "everything must be stateless."** Databases are stateful. Message brokers are stateful. WebSocket connection managers are stateful. Cache clusters are stateful. The architecture is stateless at the application tier, not at every tier.
|
|
38
|
+
- **Not "JWT everywhere."** JWT is one tool for carrying state on the client side. It is not the only tool, and it introduces its own significant problems (revocation, token bloat, security). Server-side sessions stored in Redis are equally "stateless" from the application process's perspective.
|
|
39
|
+
- **Not a performance optimization.** Externalizing state to Redis adds a network hop (~0.5-2ms) that did not exist when state was in-process memory (~0.001ms). You accept this latency in exchange for horizontal scalability and fault tolerance.
|
|
40
|
+
|
|
41
|
+
**Common misconceptions that cause real damage:**
|
|
42
|
+
|
|
43
|
+
1. "Stateless means we don't need sessions." You still need sessions. You need them externalized, not eliminated. Users still log in, maintain shopping carts, and navigate multi-step workflows.
|
|
44
|
+
2. "JWT solves all session problems." JWT creates new problems — token revocation requires server-side state (a denylist), token bloat increases bandwidth on every request, and storing sensitive data in client-readable tokens is a security risk.
|
|
45
|
+
3. "Stateless is always better." A single-server application with 100 users gains nothing from stateless design. The complexity of external session stores and token management is not free. Stateless design pays off at scale.
|
|
46
|
+
4. "If we use containers, we're automatically stateless." Containers can hold in-process state just as easily as VMs. Containerization is orthogonal to statelessness. A container running a Rails app with in-memory sessions is stateful.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## When to Use It
|
|
51
|
+
|
|
52
|
+
### Web APIs and REST services
|
|
53
|
+
|
|
54
|
+
REST is stateless by definition — each request from a client must contain all the information necessary for the server to fulfill the request. Any service exposed as a REST API should be designed stateless. Authentication travels via bearer tokens or API keys in the `Authorization` header. Pagination state travels via query parameters or cursor tokens. The server needs no memory of previous requests.
|
|
55
|
+
|
|
56
|
+
### Services behind a load balancer
|
|
57
|
+
|
|
58
|
+
The moment you put two or more instances behind a load balancer, in-process state becomes a problem. User A logs in and their session is stored in Server 1's memory. Their next request routes to Server 2, which has no knowledge of the session. The options are: sticky sessions (fragile, defeats the purpose of load balancing), session replication (expensive, complex), or stateless design (externalize the session). Stateless wins at every scale beyond one server.
|
|
59
|
+
|
|
60
|
+
### Microservices and containerized workloads
|
|
61
|
+
|
|
62
|
+
Each microservice instance should be interchangeable. Kubernetes schedules pods across nodes, kills and restarts them for health checks, and scales replicas up and down. If a pod holds in-process state, every lifecycle event is a data loss event. Stateless services treat pods as cattle, not pets.
|
|
63
|
+
|
|
64
|
+
**Netflix:** Over 700 stateless microservices running on AWS, each horizontally scaled independently. Any instance of any service can handle any request. This architecture handles over 2 billion API requests daily. The stateless design is what makes Netflix's massive horizontal scaling possible — they can spin up hundreds of instances of the recommendation service during peak hours and tear them down at night.
|
|
65
|
+
|
|
66
|
+
### Serverless and Lambda functions
|
|
67
|
+
|
|
68
|
+
Serverless functions (AWS Lambda, Google Cloud Functions, Azure Functions) are stateless by design — the runtime creates and destroys function instances at will. You have no control over which instance handles a request, and instances may be recycled between invocations. Any state must live in external stores (DynamoDB, S3, Redis). If your function relies on in-process state, it will produce inconsistent results.
|
|
69
|
+
|
|
70
|
+
### Services requiring zero-downtime deployment
|
|
71
|
+
|
|
72
|
+
Blue-green deployments, rolling updates, and canary releases all require that requests can be handled by any version of the service during the transition period. If Server A (old version) holds session state that Server B (new version) cannot access, the deployment will drop users mid-session. Stateless services with externalized state transition seamlessly — the new version reads the same Redis cluster or database as the old version.
|
|
73
|
+
|
|
74
|
+
### Multi-region and edge deployments
|
|
75
|
+
|
|
76
|
+
Services deployed across multiple geographic regions must handle requests from any region. A user in Tokyo should not be pinned to a specific server in the Tokyo region — any server should handle any request. Stateless design with a distributed external store (DynamoDB Global Tables, CockroachDB, Redis Cluster with cross-region replication) enables true global deployment.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## When NOT to Use It
|
|
81
|
+
|
|
82
|
+
**This section is deliberately comprehensive because the failure mode of forcing statelessness onto inherently stateful systems creates unnecessary complexity, degrades performance, and solves problems that do not exist.**
|
|
83
|
+
|
|
84
|
+
### WebSocket servers managing persistent connections
|
|
85
|
+
|
|
86
|
+
WebSocket connections are inherently stateful — a bidirectional channel between a specific client and a specific server. Chat applications, real-time collaboration tools (Google Docs, Figma), multiplayer games, and live dashboards all maintain per-connection state: the connection itself, subscription lists, user presence, in-flight messages.
|
|
87
|
+
|
|
88
|
+
Forcing statelessness here means: every WebSocket message would need to carry full authentication and context, connection state would need to be stored externally and read on every message (adding latency to a real-time protocol), and load balancers would need to route reconnections to the correct session state. The standard approach is to keep WebSocket servers stateful while making the REST API tier stateless. Use a pub/sub system (Redis Pub/Sub, NATS) to coordinate between stateful WebSocket servers.
|
|
89
|
+
|
|
90
|
+
### In-memory caches where latency is critical
|
|
91
|
+
|
|
92
|
+
An in-process cache serves data in ~0.001ms. An external cache (Redis) serves data in ~0.5-2ms. For most applications, this difference is invisible. But for hot-path operations executed thousands of times per second — template rendering, configuration lookups, frequently accessed reference data — the 500x latency difference matters.
|
|
93
|
+
|
|
94
|
+
**Real example:** A high-frequency trading platform moved its price cache from in-process to Redis to achieve "statelessness." Per-request latency increased by 1.5ms. On a system executing 10,000 price checks per second, this added 15 seconds of aggregate latency per second — the system could no longer keep up with the market data feed. They moved back to in-process caching with a pub/sub invalidation pattern, accepting that the service was "stateful" for cache purposes while remaining stateless for all other state.
|
|
95
|
+
|
|
96
|
+
### Workflow engines and long-running processes
|
|
97
|
+
|
|
98
|
+
Workflow engines (Temporal, Camunda, Apache Airflow) maintain process state by design — which step is active, what data has been accumulated, what compensating actions to take on failure. A three-day approval workflow cannot be stateless because the state IS the workflow. The workflow engine is explicitly and correctly a stateful service.
|
|
99
|
+
|
|
100
|
+
Attempting to make workflow state external and load it on every step adds enormous complexity with no benefit. The workflow engine already persists state to a database — that is its core responsibility.
|
|
101
|
+
|
|
102
|
+
### Single-server applications with modest traffic
|
|
103
|
+
|
|
104
|
+
A side project, internal tool, or small business application serving 50-500 users from a single server gains nothing from stateless design. In-process sessions are simpler, faster, and perfectly adequate. The added complexity of an external Redis instance, JWT token management, or database-backed sessions is overhead that provides no return at this scale.
|
|
105
|
+
|
|
106
|
+
**The threshold:** When you need your second server, refactor for statelessness. Not before.
|
|
107
|
+
|
|
108
|
+
### Machine learning inference with model state
|
|
109
|
+
|
|
110
|
+
ML model serving (TensorFlow Serving, PyTorch inference servers) loads models into GPU memory. These models can be gigabytes in size. Reloading a 7GB model from external storage on every request is not viable. The model state is in-process by necessity. Scaling is achieved by running multiple stateful instances, each with its own loaded model, behind a load balancer — the model state is identical across instances, so any instance can handle any request even though each instance is technically stateful.
|
|
111
|
+
|
|
112
|
+
### Gaming servers with physics state
|
|
113
|
+
|
|
114
|
+
Multiplayer game servers maintain authoritative game state — player positions, physics simulations, entity states. This state changes 60 times per second. Externalizing it to Redis or a database at 60Hz with 100 entities would saturate the network and add unacceptable latency. Game servers are correctly stateful, with state persistence happening at lower frequency (checkpoints, save points) and reconnection protocols handling server failures.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## How It Works
|
|
119
|
+
|
|
120
|
+
### Session externalization strategies
|
|
121
|
+
|
|
122
|
+
The core mechanism of stateless design is moving session state out of the application process. There are three primary strategies, each with distinct trade-offs:
|
|
123
|
+
|
|
124
|
+
**Strategy 1: Server-side sessions in an external store (Redis/Memcached)**
|
|
125
|
+
|
|
126
|
+
The application generates a random session ID, stores session data in Redis keyed by that ID, and sends the session ID to the client in a cookie. On subsequent requests, the client sends the cookie, and the application looks up the session in Redis.
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
Client App Server (stateless) Redis (stateful)
|
|
130
|
+
│ │ │
|
|
131
|
+
├── POST /login ──────────────>│ │
|
|
132
|
+
│ ├── Authenticate user │
|
|
133
|
+
│ ├── SET session:abc123 {...} ───>│
|
|
134
|
+
│ │ ├── Store
|
|
135
|
+
│<── Set-Cookie: sid=abc123 ──-│ │
|
|
136
|
+
│ │ │
|
|
137
|
+
├── GET /dashboard ───────────>│ │
|
|
138
|
+
│ Cookie: sid=abc123 ├── GET session:abc123 ─────────>│
|
|
139
|
+
│ │<── {user_id: 42, role: admin} ─│
|
|
140
|
+
│ ├── Process request │
|
|
141
|
+
│<── 200 OK ──────────────────-│ │
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Advantages:** Small cookie size (~20 bytes), full server-side control over sessions (instant revocation by deleting the key), session data is opaque to the client (security), well-supported by every web framework.
|
|
145
|
+
|
|
146
|
+
**Disadvantages:** Redis becomes a dependency for every authenticated request, adds ~0.5-2ms per request for the Redis lookup, Redis itself must be highly available (Redis Sentinel or Redis Cluster).
|
|
147
|
+
|
|
148
|
+
**Strategy 2: Client-side tokens (JWT)**
|
|
149
|
+
|
|
150
|
+
The application encodes session data directly into a signed token (JWT) and sends it to the client. The client sends the token with every request. The server validates the signature and reads the claims — no external lookup required.
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
Client App Server (stateless) Nothing needed
|
|
154
|
+
│ │
|
|
155
|
+
├── POST /login ──────────────>│
|
|
156
|
+
│ ├── Authenticate user
|
|
157
|
+
│ ├── Sign JWT {user_id: 42, role: admin, exp: ...}
|
|
158
|
+
│<── Authorization: Bearer eyJ.──│
|
|
159
|
+
│ │
|
|
160
|
+
├── GET /dashboard ───────────>│
|
|
161
|
+
│ Authorization: Bearer eyJ. │
|
|
162
|
+
│ ├── Verify signature
|
|
163
|
+
│ ├── Read claims from token
|
|
164
|
+
│ ├── Process request
|
|
165
|
+
│<── 200 OK ──────────────────-│
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Advantages:** No external session store needed (truly stateless), no network hop for session lookup, works across domains and services (single sign-on), scales infinitely (no shared state).
|
|
169
|
+
|
|
170
|
+
**Disadvantages:** Token revocation is impossible without server-side state (a denylist — which defeats the statelessness). Token bloat: adding roles, permissions, and user data to the JWT increases its size, and this payload is sent with every request. Sensitive data exposure: JWT payloads are Base64-encoded (not encrypted) — anyone can read them. Token lifetime management: short-lived tokens require refresh token flows, adding complexity.
|
|
171
|
+
|
|
172
|
+
**Strategy 3: Encrypted cookies (server-side secrets, client-side storage)**
|
|
173
|
+
|
|
174
|
+
The application serializes session data, encrypts it with a server-side key, and stores it in a cookie. The cookie is opaque to the client. On subsequent requests, the server decrypts the cookie and reads the session data.
|
|
175
|
+
|
|
176
|
+
**Advantages:** No external store, session data is encrypted (unlike JWT), cookie-native (automatic browser handling), easy revocation by rotating the encryption key (invalidates all sessions).
|
|
177
|
+
|
|
178
|
+
**Disadvantages:** Cookie size limits (4KB per cookie, ~20KB total across all cookies), encrypting/decrypting on every request has CPU cost, rotating the key invalidates ALL sessions (not individual ones).
|
|
179
|
+
|
|
180
|
+
### The JWT vs. server-side sessions debate
|
|
181
|
+
|
|
182
|
+
This is one of the most contentious design decisions in web architecture. The honest answer: neither is universally correct.
|
|
183
|
+
|
|
184
|
+
**Use JWT when:**
|
|
185
|
+
- Services need to validate authentication without calling a central auth service (microservices with decentralized auth)
|
|
186
|
+
- The system spans multiple domains where cookies cannot be shared
|
|
187
|
+
- The session data is small (user ID, role — under 200 bytes of claims)
|
|
188
|
+
- Token lifetime is short (5-15 minutes) with refresh tokens for extended sessions
|
|
189
|
+
- Immediate revocation is not a hard requirement (or you accept a revocation delay equal to token lifetime)
|
|
190
|
+
|
|
191
|
+
**Use server-side sessions (Redis) when:**
|
|
192
|
+
- Immediate session revocation is required (security-critical applications, banking, healthcare)
|
|
193
|
+
- Session data is large or changes frequently (shopping carts, multi-step wizards)
|
|
194
|
+
- You need to enumerate active sessions ("show all logged-in devices")
|
|
195
|
+
- You need to enforce concurrent session limits ("only 3 active sessions per user")
|
|
196
|
+
- The application is a monolith or small number of services sharing a Redis cluster
|
|
197
|
+
|
|
198
|
+
**The revocation problem in detail:**
|
|
199
|
+
|
|
200
|
+
The most cited criticism of JWT for authentication is the revocation problem. Once a JWT is issued, it cannot be invalidated until it expires — the server has no record to delete, no session to terminate. If a user's account is compromised, an administrator cannot instantly lock them out.
|
|
201
|
+
|
|
202
|
+
The workarounds all add state back into the "stateless" system:
|
|
203
|
+
|
|
204
|
+
1. **Token denylist (blacklist):** Store revoked token IDs in Redis. Check the denylist on every request. This is exactly a session store — you have re-invented server-side sessions with extra steps.
|
|
205
|
+
2. **Short-lived tokens + refresh tokens:** Issue access tokens with 5-15 minute lifetimes. Use refresh tokens (stored server-side) to issue new access tokens. Revocation happens by invalidating the refresh token. The security gap is the access token lifetime — a compromised token remains valid for up to 15 minutes.
|
|
206
|
+
3. **Token versioning:** Store a "token version" counter per user in the database. Increment it on revocation. Tokens with an older version are rejected. This requires a database lookup on every request — again, adding state.
|
|
207
|
+
|
|
208
|
+
### Request context: passing state via headers and tokens
|
|
209
|
+
|
|
210
|
+
In a stateless architecture, request context travels with the request rather than being stored on the server. Common patterns:
|
|
211
|
+
|
|
212
|
+
- **Authorization header:** `Bearer <jwt>` or `Basic <credentials>` — authenticates every request independently
|
|
213
|
+
- **X-Request-ID header:** Unique identifier for distributed tracing — allows correlating logs across services without server-side tracking state
|
|
214
|
+
- **X-Correlation-ID header:** Groups related requests (e.g., all API calls triggered by one user action) — passed through service-to-service calls
|
|
215
|
+
- **Accept-Language / X-Locale header:** Localization preferences — no server-side user preference lookup needed
|
|
216
|
+
- **If-None-Match / ETag headers:** Cache validation — stateless servers can validate cached responses without remembering what they previously sent
|
|
217
|
+
|
|
218
|
+
### Caching: external vs. in-process trade-offs
|
|
219
|
+
|
|
220
|
+
Stateless design does not prohibit caching — it constrains where caches live and what guarantees they provide.
|
|
221
|
+
|
|
222
|
+
**In-process cache (acceptable in stateless design):**
|
|
223
|
+
- Configuration data loaded at startup and refreshed periodically
|
|
224
|
+
- Compiled templates or regular expressions
|
|
225
|
+
- Immutable reference data (country codes, currency formats)
|
|
226
|
+
- Rule: in-process caches must be rebuildable from external sources. If the process restarts, the cache rebuilds automatically. No request depends on a specific cache state.
|
|
227
|
+
|
|
228
|
+
**External cache (Redis/Memcached):**
|
|
229
|
+
- User-specific computed data (personalized recommendations, aggregated dashboards)
|
|
230
|
+
- Database query results shared across instances
|
|
231
|
+
- Rate limiting counters (must be shared across all instances)
|
|
232
|
+
- Rule: external caches must have a cache-miss strategy. If the cache is empty (cold start, eviction, Redis restart), the application falls back to the primary data source.
|
|
233
|
+
|
|
234
|
+
**The cold start problem:** After a deployment, all application instances start with empty in-process caches. If the in-process cache held frequently accessed data (hot product listings, trending content), the first requests after deployment hit the database directly. At scale, this cache stampede can overwhelm the database. Mitigation: cache warming (pre-populate caches at startup), staggered deployments (don't restart all instances simultaneously), or external caches that survive deployments.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Trade-Offs Matrix
|
|
239
|
+
|
|
240
|
+
| Dimension | Stateless (externalized state) | Stateful (in-process state) | Winner |
|
|
241
|
+
|---|---|---|---|
|
|
242
|
+
| **Horizontal scaling** | Trivial — add instances, any handles any request | Requires sticky sessions or session replication | Stateless |
|
|
243
|
+
| **Server failure impact** | Zero — other instances continue seamlessly | Lost sessions for all users on failed server | Stateless |
|
|
244
|
+
| **Deployment complexity** | Rolling updates work naturally, no session draining | Requires connection draining, session migration | Stateless |
|
|
245
|
+
| **Per-request latency** | +0.5-2ms for external session lookup | ~0.001ms for in-process session access | Stateful |
|
|
246
|
+
| **Infrastructure cost** | Requires Redis/Memcached cluster (additional cost) | No additional infrastructure | Stateful |
|
|
247
|
+
| **Operational simplicity** | More moving parts (app + session store + monitoring) | Single process, simpler debugging | Stateful |
|
|
248
|
+
| **Session revocation** | Immediate (delete from Redis) or delayed (JWT expiry) | Immediate (delete from memory) | Stateful (Redis) / Draw (JWT) |
|
|
249
|
+
| **Multi-region support** | Natural — each region reads from local session store replica | Requires session replication across regions | Stateless |
|
|
250
|
+
| **Autoscaling** | Scale to zero and back without session loss | Cannot scale to zero without losing all sessions | Stateless |
|
|
251
|
+
| **Cold start performance** | Empty caches after restart, potential stampede | Warm caches lost on restart (same problem, smaller scale) | Draw |
|
|
252
|
+
| **Debugging session issues** | Must query external store, cross-reference request IDs | Inspect process memory directly | Stateful |
|
|
253
|
+
| **Security surface** | JWT payloads readable by clients; Redis needs securing | Session data never leaves the server | Stateful |
|
|
254
|
+
|
|
255
|
+
**The honest summary:** Stateless design wins decisively for systems that need to scale beyond a single server, survive failures, or deploy without downtime. Stateful design wins for single-server systems where simplicity and raw performance matter more than scalability. The crossover point is approximately the moment you add a second server instance.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Evolution Path
|
|
260
|
+
|
|
261
|
+
### Stage 1: Single server with in-process sessions (correct starting point)
|
|
262
|
+
|
|
263
|
+
For a new application, start with the simplest session management your framework provides — typically in-process sessions stored in memory. Rails uses `CookieStore` by default (encrypted cookies), Express uses `express-session` with `MemoryStore`, Django uses database-backed sessions. This is fine. Ship the product.
|
|
264
|
+
|
|
265
|
+
At this stage, you have one server, modest traffic, and no need for horizontal scaling. In-process sessions add zero latency and zero infrastructure. Do not add Redis "because we might scale later." YAGNI.
|
|
266
|
+
|
|
267
|
+
### Stage 2: External session store (when you add the second server)
|
|
268
|
+
|
|
269
|
+
The trigger: you need a second server instance — for load balancing, high availability, or increased capacity. At this point, in-process sessions break because requests may route to either server.
|
|
270
|
+
|
|
271
|
+
**Migration path:**
|
|
272
|
+
1. Deploy Redis (or use a managed service: AWS ElastiCache, Google Memorystore)
|
|
273
|
+
2. Change your session store configuration from in-memory to Redis (typically a one-line configuration change in most frameworks)
|
|
274
|
+
3. Deploy the updated application — sessions are now shared across all instances
|
|
275
|
+
4. Add the second server behind the load balancer
|
|
276
|
+
|
|
277
|
+
This migration is usually trivial because web frameworks abstract session storage behind a common interface. Switching from `MemoryStore` to `RedisStore` in Express, or from `cache` to `redis` session driver in Laravel, requires no application code changes.
|
|
278
|
+
|
|
279
|
+
### Stage 3: Token-based authentication (when services multiply)
|
|
280
|
+
|
|
281
|
+
The trigger: you now have multiple services (API server, background workers, mobile app backend) that all need to authenticate the same users. Sharing a Redis session store across all services is possible but creates tight coupling — every service depends on the same Redis cluster with the same session format.
|
|
282
|
+
|
|
283
|
+
**Migration path:**
|
|
284
|
+
1. Implement an authentication service that issues JWTs on login
|
|
285
|
+
2. Each service validates JWTs independently using the shared public key (no network call to auth service)
|
|
286
|
+
3. Use short-lived access tokens (15 minutes) with refresh tokens (stored in the auth service's database)
|
|
287
|
+
4. Keep Redis for service-specific session data that is too large for JWTs (shopping cart state, wizard progress)
|
|
288
|
+
|
|
289
|
+
### Stage 4: Stateless at the edge (when going global)
|
|
290
|
+
|
|
291
|
+
The trigger: you deploy to multiple regions and need authentication validation at the edge (CDN, API gateway) without calling back to a central auth service.
|
|
292
|
+
|
|
293
|
+
**Migration path:**
|
|
294
|
+
1. API gateway validates JWTs at the edge using the public key (no network round-trip to the origin)
|
|
295
|
+
2. User-specific data is cached in regional Redis clusters with cross-region replication
|
|
296
|
+
3. Write operations route to the primary region; reads serve from local replicas
|
|
297
|
+
4. Token refresh still routes to the auth service, but access token validation is fully distributed
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Failure Modes
|
|
302
|
+
|
|
303
|
+
### Failure Mode 1: JWT token bloat
|
|
304
|
+
|
|
305
|
+
**What happens:** The team starts adding data to JWT claims — user roles, permissions, feature flags, organization details, subscription tier, user preferences. The token grows from 200 bytes to 4KB. This token is sent with every HTTP request in the `Authorization` header.
|
|
306
|
+
|
|
307
|
+
**The math:** 4KB token x 100 requests per page load x 10,000 concurrent users = 4GB of bandwidth consumed just by tokens. For mobile users on metered connections, this is especially costly. HTTP/2 header compression (HPACK) helps but cannot eliminate the overhead entirely.
|
|
308
|
+
|
|
309
|
+
**Prevention:** JWTs should contain only identity claims — user ID, issuer, expiration, and at most a role or scope string. Everything else should be looked up server-side using the user ID from the token. If your JWT exceeds 1KB, you are storing too much in it.
|
|
310
|
+
|
|
311
|
+
### Failure Mode 2: The JWT revocation gap
|
|
312
|
+
|
|
313
|
+
**What happens:** A user's account is compromised. The security team needs to lock them out immediately. But the user holds a valid JWT with a 60-minute expiration. For the next 60 minutes, the compromised token grants full access to every service that validates it.
|
|
314
|
+
|
|
315
|
+
**Real impact:** Financial services, healthcare, and any application handling sensitive data cannot accept a 60-minute revocation gap. This is not a theoretical concern — it is a compliance blocker for SOC 2, HIPAA, and PCI DSS.
|
|
316
|
+
|
|
317
|
+
**Prevention:** Use short-lived access tokens (5-15 minutes maximum) with refresh tokens. Accept that refresh token validation requires a server-side lookup. For immediate revocation requirements, maintain a token denylist in Redis — accept the trade-off that you are adding state back into the system. For truly critical systems, use server-side sessions in Redis instead of JWTs.
|
|
318
|
+
|
|
319
|
+
### Failure Mode 3: External session store as single point of failure
|
|
320
|
+
|
|
321
|
+
**What happens:** The team externalizes all sessions to a single Redis instance. Redis goes down. Every user is instantly logged out. Every authenticated request fails. The stateless application servers are running perfectly, but they cannot validate any session.
|
|
322
|
+
|
|
323
|
+
**The irony:** The team adopted stateless design to improve reliability. By centralizing all session state in a single Redis instance, they created a single point of failure more catastrophic than any individual application server failure in the old stateful design.
|
|
324
|
+
|
|
325
|
+
**Prevention:** Redis Sentinel (automatic failover) or Redis Cluster (distributed, no single point of failure). Managed services (AWS ElastiCache, Google Memorystore) handle this automatically. Always deploy session stores with replication and automatic failover. Test the failover path — do not assume it works because the vendor says it does.
|
|
326
|
+
|
|
327
|
+
### Failure Mode 4: Cache stampede after deployment
|
|
328
|
+
|
|
329
|
+
**What happens:** The team deploys a new version, restarting all application instances simultaneously. All in-process caches are empty. The first wave of requests hits the database directly. If the application serves 10,000 requests per second and 80% normally hit the cache, 8,000 requests per second suddenly hit the database. The database collapses under the unexpected load.
|
|
330
|
+
|
|
331
|
+
**Prevention:** Staggered rolling deployments (restart instances one at a time, allowing each to warm its cache before the next restarts). Cache warming at startup (pre-populate hot cache entries from the database or external cache before accepting traffic). External caches (Redis) that survive deployments — even if in-process caches are cold, the Redis cache is warm.
|
|
332
|
+
|
|
333
|
+
### Failure Mode 5: Stateless services with stateful dependencies creating hidden coupling
|
|
334
|
+
|
|
335
|
+
**What happens:** The application servers are stateless, but they all depend on the same Redis cluster for sessions and the same PostgreSQL primary for writes. During a Redis network partition or PostgreSQL failover, all "stateless" servers fail simultaneously. The architecture appears distributed but has the same failure domain as a monolith.
|
|
336
|
+
|
|
337
|
+
**Prevention:** Circuit breakers on external state dependencies — if Redis is unreachable, degrade gracefully (allow unauthenticated access to public endpoints, queue writes). Regional independence — each region has its own Redis replica and database read replica. Distinguish between hard dependencies (authentication must work) and soft dependencies (personalization can degrade).
|
|
338
|
+
|
|
339
|
+
### Failure Mode 6: Sensitive data in JWT payloads
|
|
340
|
+
|
|
341
|
+
**What happens:** The team stores user email, phone number, internal user IDs, or permission details in JWT claims. JWT payloads are Base64-encoded, not encrypted — anyone with the token can decode the payload and read all claims. If tokens are logged, cached in CDNs, or stored in browser local storage, sensitive data is exposed.
|
|
342
|
+
|
|
343
|
+
**Prevention:** JWTs should contain only non-sensitive identifiers (opaque user ID, not email). Sensitive data should be looked up server-side. If you must include sensitive data in client-side tokens, use JWE (JSON Web Encryption) instead of JWS (JSON Web Signature). Better yet, use encrypted server-side sessions where the client only holds an opaque session ID.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Technology Landscape
|
|
348
|
+
|
|
349
|
+
### JWT libraries and standards
|
|
350
|
+
|
|
351
|
+
| Technology | Language | Notes |
|
|
352
|
+
|---|---|---|
|
|
353
|
+
| `jsonwebtoken` (npm) | Node.js | Most popular JWT library for Node. Supports RS256, HS256. Use RS256 for multi-service validation. |
|
|
354
|
+
| `PyJWT` | Python | Standard Python JWT library. Use with `cryptography` package for RS256. |
|
|
355
|
+
| `jjwt` (Java JWT) | Java | Fluent API for JWT creation and validation. Spring Security integrates natively. |
|
|
356
|
+
| `golang-jwt/jwt` | Go | Community-maintained fork of `dgrijalva/jwt-go`. Standard for Go services. |
|
|
357
|
+
| `System.IdentityModel.Tokens.Jwt` | .NET | Microsoft's official JWT library. Integrates with ASP.NET Core authentication middleware. |
|
|
358
|
+
|
|
359
|
+
**Key decisions:**
|
|
360
|
+
- **Algorithm:** Use RS256 (asymmetric) for multi-service architectures — services validate with the public key without needing the signing secret. Use HS256 (symmetric) only for single-service scenarios.
|
|
361
|
+
- **Token lifetime:** Access tokens: 5-15 minutes. Refresh tokens: 7-30 days (stored server-side with rotation on use).
|
|
362
|
+
- **Key rotation:** Use JWKS (JSON Web Key Set) endpoints for key distribution. Rotate signing keys quarterly. Old keys must remain in the JWKS endpoint until all tokens signed with them expire.
|
|
363
|
+
|
|
364
|
+
### External session stores
|
|
365
|
+
|
|
366
|
+
| Technology | Latency | Persistence | Clustering | Best for |
|
|
367
|
+
|---|---|---|---|---|
|
|
368
|
+
| **Redis** | <1ms | Optional (RDB/AOF) | Redis Cluster, Sentinel | Most session management use cases. Sub-millisecond reads. |
|
|
369
|
+
| **Memcached** | <1ms | None | Client-side sharding | Simple key-value sessions where persistence is unnecessary. |
|
|
370
|
+
| **DynamoDB** | 1-5ms | Durable | Managed, auto-scaling | AWS-native applications needing zero operational overhead. |
|
|
371
|
+
| **PostgreSQL** | 2-10ms | Durable | Read replicas | Applications already using PostgreSQL that want to avoid adding Redis. |
|
|
372
|
+
| **Valkey** | <1ms | Optional (RDB/AOF) | Cluster, Sentinel | Open-source Redis fork. Drop-in replacement since Redis license change. |
|
|
373
|
+
|
|
374
|
+
**Redis is the default choice** for session management. It handles thousands of concurrent session operations with sub-millisecond latency, supports automatic expiration (TTL) for sessions, and every major web framework has a Redis session adapter. Deploy with Sentinel or Cluster for high availability.
|
|
375
|
+
|
|
376
|
+
### Cookie-based session frameworks
|
|
377
|
+
|
|
378
|
+
| Framework | Default session store | External store support | Session type |
|
|
379
|
+
|---|---|---|---|
|
|
380
|
+
| **Rails** | Encrypted cookie (CookieStore) | Redis, Memcached, DB via adapters | Server-side or encrypted client-side |
|
|
381
|
+
| **Express (Node.js)** | MemoryStore (development only) | `connect-redis`, `connect-mongo` | Server-side with cookie ID |
|
|
382
|
+
| **Django** | Database | Redis via `django-redis`, cache, file | Server-side |
|
|
383
|
+
| **Spring Boot** | In-memory (Tomcat) | Spring Session + Redis/JDBC | Server-side |
|
|
384
|
+
| **Laravel** | File | Redis, database, Memcached, cookie | Server-side or encrypted cookie |
|
|
385
|
+
| **ASP.NET Core** | In-memory | Redis, SQL Server via IDistributedCache | Server-side |
|
|
386
|
+
|
|
387
|
+
### Authentication and identity platforms
|
|
388
|
+
|
|
389
|
+
| Platform | Type | Stateless support | Notes |
|
|
390
|
+
|---|---|---|---|
|
|
391
|
+
| **Auth0** | Managed identity | JWT-based, JWKS endpoint | Fully managed. Issues JWTs validated by your services via public key. |
|
|
392
|
+
| **Keycloak** | Self-hosted identity | OIDC + JWT | Open-source. Full OAuth2/OIDC provider. Heavier operational burden. |
|
|
393
|
+
| **Firebase Auth** | Managed identity | JWT (Firebase ID tokens) | Simple integration for mobile/web apps. Google-ecosystem. |
|
|
394
|
+
| **AWS Cognito** | Managed identity | JWT + JWKS | AWS-native. Integrates with API Gateway for edge validation. |
|
|
395
|
+
| **Supabase Auth** | Managed identity | JWT-based | PostgreSQL-native. Open-source alternative to Firebase Auth. |
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Decision Tree
|
|
400
|
+
|
|
401
|
+
```
|
|
402
|
+
Do you need more than one server instance?
|
|
403
|
+
│
|
|
404
|
+
├── No (single server, modest traffic)
|
|
405
|
+
│ └── Use in-process sessions. Keep it simple.
|
|
406
|
+
│ Framework defaults are fine (Rails CookieStore,
|
|
407
|
+
│ Express MemoryStore, Django DB sessions).
|
|
408
|
+
│ Revisit when you add a second server.
|
|
409
|
+
│
|
|
410
|
+
└── Yes (load balanced, autoscaling, multi-instance)
|
|
411
|
+
│
|
|
412
|
+
├── Is this a monolith or small number of services (1-3)?
|
|
413
|
+
│ │
|
|
414
|
+
│ ├── Yes
|
|
415
|
+
│ │ └── Use Redis-backed server-side sessions.
|
|
416
|
+
│ │ Change your framework's session store to Redis.
|
|
417
|
+
│ │ One-line config change in most frameworks.
|
|
418
|
+
│ │ Full server-side control, instant revocation.
|
|
419
|
+
│ │
|
|
420
|
+
│ └── No (microservices, 4+ services)
|
|
421
|
+
│ │
|
|
422
|
+
│ ├── Do all services share the same Redis cluster?
|
|
423
|
+
│ │ ├── Yes, and this is acceptable coupling
|
|
424
|
+
│ │ │ └── Shared Redis sessions are fine.
|
|
425
|
+
│ │ │ Simpler than JWT. Instant revocation.
|
|
426
|
+
│ │ │
|
|
427
|
+
│ │ └── No, services need independent auth validation
|
|
428
|
+
│ │ │
|
|
429
|
+
│ │ ├── Is immediate token revocation required?
|
|
430
|
+
│ │ │ ├── Yes (banking, healthcare, compliance)
|
|
431
|
+
│ │ │ │ └── JWT with short lifetime (5-15 min)
|
|
432
|
+
│ │ │ │ + refresh tokens (server-side)
|
|
433
|
+
│ │ │ │ + token denylist in Redis for emergencies.
|
|
434
|
+
│ │ │ │ Accept the hybrid: mostly stateless,
|
|
435
|
+
│ │ │ │ denylist adds minimal state.
|
|
436
|
+
│ │ │ │
|
|
437
|
+
│ │ │ └── No (revocation gap of 5-15 min acceptable)
|
|
438
|
+
│ │ │ └── Pure JWT with short-lived access tokens.
|
|
439
|
+
│ │ │ Refresh tokens for session continuity.
|
|
440
|
+
│ │ │ No server-side session store needed.
|
|
441
|
+
│ │ │
|
|
442
|
+
│ │ └── Do you need single sign-on across domains?
|
|
443
|
+
│ │ ├── Yes → JWT or OIDC tokens (cross-domain capable)
|
|
444
|
+
│ │ └── No → Redis sessions are simpler and more secure.
|
|
445
|
+
│
|
|
446
|
+
├── Is this a serverless / Lambda architecture?
|
|
447
|
+
│ └── You MUST be stateless. Lambda instances are ephemeral.
|
|
448
|
+
│ Use JWT for authentication.
|
|
449
|
+
│ Use DynamoDB or Redis for any persistent state.
|
|
450
|
+
│ No in-process state survives between invocations.
|
|
451
|
+
│
|
|
452
|
+
└── Do you need edge/CDN authentication validation?
|
|
453
|
+
├── Yes → JWT validated at the edge (API Gateway, CloudFront).
|
|
454
|
+
│ No round-trip to origin for auth. Public key
|
|
455
|
+
│ distributed to edge locations via JWKS.
|
|
456
|
+
│
|
|
457
|
+
└── No → Redis-backed sessions. Simpler, more secure,
|
|
458
|
+
instant revocation. JWT complexity not justified.
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Summary decision heuristic:**
|
|
462
|
+
|
|
463
|
+
- Single server, <500 users: in-process sessions (do not over-engineer)
|
|
464
|
+
- Multi-server monolith: Redis-backed sessions (simplest correct solution)
|
|
465
|
+
- Microservices, same trust boundary: shared Redis sessions
|
|
466
|
+
- Microservices, cross-domain or independent validation: JWT with short lifetimes
|
|
467
|
+
- Serverless: JWT + external state store (no choice, Lambda is ephemeral)
|
|
468
|
+
- Edge authentication: JWT (only option that works without origin round-trip)
|
|
469
|
+
- Immediate revocation required: Redis sessions or JWT + denylist (pure JWT is insufficient)
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Implementation Sketch
|
|
474
|
+
|
|
475
|
+
### Express.js: Redis-backed sessions (recommended for most applications)
|
|
476
|
+
|
|
477
|
+
```javascript
|
|
478
|
+
// session-setup.js — stateless application servers, state in Redis
|
|
479
|
+
const express = require('express');
|
|
480
|
+
const session = require('express-session');
|
|
481
|
+
const RedisStore = require('connect-redis').default;
|
|
482
|
+
const { createClient } = require('redis');
|
|
483
|
+
|
|
484
|
+
const app = express();
|
|
485
|
+
|
|
486
|
+
// Redis client with reconnection and error handling
|
|
487
|
+
const redisClient = createClient({
|
|
488
|
+
url: process.env.REDIS_URL || 'redis://localhost:6379',
|
|
489
|
+
socket: {
|
|
490
|
+
reconnectStrategy: (retries) => Math.min(retries * 100, 5000),
|
|
491
|
+
},
|
|
492
|
+
});
|
|
493
|
+
redisClient.on('error', (err) => console.error('Redis error:', err));
|
|
494
|
+
redisClient.connect();
|
|
495
|
+
|
|
496
|
+
// Session middleware — state is entirely in Redis, not in the process
|
|
497
|
+
app.use(session({
|
|
498
|
+
store: new RedisStore({ client: redisClient }),
|
|
499
|
+
secret: process.env.SESSION_SECRET, // sign the session cookie
|
|
500
|
+
resave: false, // don't save unchanged sessions
|
|
501
|
+
saveUninitialized: false, // don't create empty sessions
|
|
502
|
+
cookie: {
|
|
503
|
+
secure: process.env.NODE_ENV === 'production', // HTTPS only in prod
|
|
504
|
+
httpOnly: true, // no JavaScript access
|
|
505
|
+
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
|
506
|
+
sameSite: 'lax', // CSRF protection
|
|
507
|
+
},
|
|
508
|
+
}));
|
|
509
|
+
|
|
510
|
+
// This server is stateless — kill it, restart it, scale to 100 instances.
|
|
511
|
+
// All session data lives in Redis. Any instance handles any request.
|
|
512
|
+
|
|
513
|
+
app.post('/login', async (req, res) => {
|
|
514
|
+
const user = await authenticateUser(req.body.email, req.body.password);
|
|
515
|
+
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
|
|
516
|
+
|
|
517
|
+
// Store session data in Redis (not in this process's memory)
|
|
518
|
+
req.session.userId = user.id;
|
|
519
|
+
req.session.role = user.role;
|
|
520
|
+
res.json({ message: 'Logged in' });
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
app.post('/logout', (req, res) => {
|
|
524
|
+
// Immediate revocation — delete the session from Redis
|
|
525
|
+
req.session.destroy((err) => {
|
|
526
|
+
if (err) return res.status(500).json({ error: 'Logout failed' });
|
|
527
|
+
res.clearCookie('connect.sid');
|
|
528
|
+
res.json({ message: 'Logged out' });
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
app.get('/dashboard', requireAuth, (req, res) => {
|
|
533
|
+
// req.session.userId was loaded from Redis, not from this process
|
|
534
|
+
res.json({ userId: req.session.userId, role: req.session.role });
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
function requireAuth(req, res, next) {
|
|
538
|
+
if (!req.session.userId) return res.status(401).json({ error: 'Not authenticated' });
|
|
539
|
+
next();
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### JWT authentication with refresh tokens (for microservices)
|
|
544
|
+
|
|
545
|
+
```javascript
|
|
546
|
+
// auth-service.js — issues tokens. This is the ONLY service that touches credentials.
|
|
547
|
+
const jwt = require('jsonwebtoken');
|
|
548
|
+
const crypto = require('crypto');
|
|
549
|
+
|
|
550
|
+
const ACCESS_TOKEN_EXPIRY = '15m'; // Short-lived — limits revocation gap
|
|
551
|
+
const REFRESH_TOKEN_EXPIRY = '7d'; // Long-lived — stored server-side
|
|
552
|
+
|
|
553
|
+
async function login(email, password) {
|
|
554
|
+
const user = await authenticateUser(email, password);
|
|
555
|
+
if (!user) throw new Error('Invalid credentials');
|
|
556
|
+
|
|
557
|
+
// Access token: stateless, validated by any service with the public key
|
|
558
|
+
const accessToken = jwt.sign(
|
|
559
|
+
{ sub: user.id, role: user.role },
|
|
560
|
+
process.env.JWT_PRIVATE_KEY,
|
|
561
|
+
{ algorithm: 'RS256', expiresIn: ACCESS_TOKEN_EXPIRY }
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
// Refresh token: stored server-side for revocation control
|
|
565
|
+
const refreshToken = crypto.randomBytes(64).toString('hex');
|
|
566
|
+
await redis.set(
|
|
567
|
+
`refresh:${refreshToken}`,
|
|
568
|
+
JSON.stringify({ userId: user.id, createdAt: Date.now() }),
|
|
569
|
+
{ EX: 7 * 24 * 60 * 60 } // 7 days TTL
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
return { accessToken, refreshToken };
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
async function refresh(refreshToken) {
|
|
576
|
+
// Server-side lookup — this is where revocation happens
|
|
577
|
+
const data = await redis.get(`refresh:${refreshToken}`);
|
|
578
|
+
if (!data) throw new Error('Invalid or expired refresh token');
|
|
579
|
+
|
|
580
|
+
const { userId } = JSON.parse(data);
|
|
581
|
+
const user = await getUserById(userId);
|
|
582
|
+
|
|
583
|
+
// Rotate refresh token (one-time use prevents token theft replay)
|
|
584
|
+
await redis.del(`refresh:${refreshToken}`);
|
|
585
|
+
const newRefreshToken = crypto.randomBytes(64).toString('hex');
|
|
586
|
+
await redis.set(
|
|
587
|
+
`refresh:${newRefreshToken}`,
|
|
588
|
+
JSON.stringify({ userId: user.id, createdAt: Date.now() }),
|
|
589
|
+
{ EX: 7 * 24 * 60 * 60 }
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
const accessToken = jwt.sign(
|
|
593
|
+
{ sub: user.id, role: user.role },
|
|
594
|
+
process.env.JWT_PRIVATE_KEY,
|
|
595
|
+
{ algorithm: 'RS256', expiresIn: ACCESS_TOKEN_EXPIRY }
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
return { accessToken, refreshToken: newRefreshToken };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async function revoke(userId) {
|
|
602
|
+
// Emergency revocation: delete all refresh tokens for this user
|
|
603
|
+
// Access tokens remain valid until expiry (max 15 minutes)
|
|
604
|
+
const keys = await redis.keys(`refresh:*`);
|
|
605
|
+
for (const key of keys) {
|
|
606
|
+
const data = await redis.get(key);
|
|
607
|
+
if (data && JSON.parse(data).userId === userId) {
|
|
608
|
+
await redis.del(key);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
```javascript
|
|
615
|
+
// middleware/auth.js — used by ANY downstream service (no Redis dependency)
|
|
616
|
+
const jwt = require('jsonwebtoken');
|
|
617
|
+
|
|
618
|
+
function verifyToken(req, res, next) {
|
|
619
|
+
const authHeader = req.headers.authorization;
|
|
620
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
621
|
+
return res.status(401).json({ error: 'Missing token' });
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
try {
|
|
625
|
+
// Validate with PUBLIC key — no shared secret, no network call
|
|
626
|
+
const decoded = jwt.verify(
|
|
627
|
+
authHeader.split(' ')[1],
|
|
628
|
+
process.env.JWT_PUBLIC_KEY,
|
|
629
|
+
{ algorithms: ['RS256'] }
|
|
630
|
+
);
|
|
631
|
+
req.user = { id: decoded.sub, role: decoded.role };
|
|
632
|
+
next();
|
|
633
|
+
} catch (err) {
|
|
634
|
+
return res.status(401).json({ error: 'Invalid or expired token' });
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### Python FastAPI: stateless with dependency injection
|
|
640
|
+
|
|
641
|
+
```python
|
|
642
|
+
# main.py — stateless FastAPI service
|
|
643
|
+
from fastapi import FastAPI, Depends, HTTPException, status
|
|
644
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
645
|
+
import jwt
|
|
646
|
+
import redis.asyncio as redis
|
|
647
|
+
from datetime import datetime, timedelta
|
|
648
|
+
import os
|
|
649
|
+
|
|
650
|
+
app = FastAPI()
|
|
651
|
+
security = HTTPBearer()
|
|
652
|
+
redis_client = redis.from_url(os.environ.get("REDIS_URL", "redis://localhost:6379"))
|
|
653
|
+
|
|
654
|
+
PUBLIC_KEY = os.environ["JWT_PUBLIC_KEY"]
|
|
655
|
+
|
|
656
|
+
async def get_current_user(
|
|
657
|
+
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
658
|
+
) -> dict:
|
|
659
|
+
"""Stateless auth — validates JWT with public key, no external call."""
|
|
660
|
+
try:
|
|
661
|
+
payload = jwt.decode(
|
|
662
|
+
credentials.credentials,
|
|
663
|
+
PUBLIC_KEY,
|
|
664
|
+
algorithms=["RS256"],
|
|
665
|
+
)
|
|
666
|
+
return {"id": payload["sub"], "role": payload["role"]}
|
|
667
|
+
except jwt.ExpiredSignatureError:
|
|
668
|
+
raise HTTPException(status_code=401, detail="Token expired")
|
|
669
|
+
except jwt.InvalidTokenError:
|
|
670
|
+
raise HTTPException(status_code=401, detail="Invalid token")
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
@app.get("/api/orders")
|
|
674
|
+
async def get_orders(user: dict = Depends(get_current_user)):
|
|
675
|
+
# This endpoint is stateless — any instance handles any request.
|
|
676
|
+
# User identity comes from the JWT, not from server memory.
|
|
677
|
+
orders = await fetch_orders_for_user(user["id"])
|
|
678
|
+
return {"orders": orders}
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
@app.get("/api/cart")
|
|
682
|
+
async def get_cart(user: dict = Depends(get_current_user)):
|
|
683
|
+
# Cart state externalized to Redis — not in this process
|
|
684
|
+
cart_data = await redis_client.get(f"cart:{user['id']}")
|
|
685
|
+
if not cart_data:
|
|
686
|
+
return {"items": []}
|
|
687
|
+
return {"items": json.loads(cart_data)}
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Kubernetes deployment: stateless service with health checks
|
|
691
|
+
|
|
692
|
+
```yaml
|
|
693
|
+
# k8s/deployment.yaml — stateless service, horizontally scalable
|
|
694
|
+
apiVersion: apps/v1
|
|
695
|
+
kind: Deployment
|
|
696
|
+
metadata:
|
|
697
|
+
name: api-server
|
|
698
|
+
spec:
|
|
699
|
+
replicas: 3 # Scale freely — all instances are identical
|
|
700
|
+
strategy:
|
|
701
|
+
type: RollingUpdate
|
|
702
|
+
rollingUpdate:
|
|
703
|
+
maxUnavailable: 1 # Always keep 2+ instances running
|
|
704
|
+
maxSurge: 1 # Add 1 new before removing 1 old
|
|
705
|
+
selector:
|
|
706
|
+
matchLabels:
|
|
707
|
+
app: api-server
|
|
708
|
+
template:
|
|
709
|
+
metadata:
|
|
710
|
+
labels:
|
|
711
|
+
app: api-server
|
|
712
|
+
spec:
|
|
713
|
+
containers:
|
|
714
|
+
- name: api
|
|
715
|
+
image: myapp/api-server:v2.1.0
|
|
716
|
+
ports:
|
|
717
|
+
- containerPort: 3000
|
|
718
|
+
env:
|
|
719
|
+
- name: REDIS_URL
|
|
720
|
+
valueFrom:
|
|
721
|
+
secretKeyRef:
|
|
722
|
+
name: redis-credentials
|
|
723
|
+
key: url
|
|
724
|
+
- name: JWT_PUBLIC_KEY
|
|
725
|
+
valueFrom:
|
|
726
|
+
configMapKeyRef:
|
|
727
|
+
name: auth-config
|
|
728
|
+
key: jwt-public-key
|
|
729
|
+
# Readiness probe: don't route traffic until Redis connection is established
|
|
730
|
+
readinessProbe:
|
|
731
|
+
httpGet:
|
|
732
|
+
path: /health/ready
|
|
733
|
+
port: 3000
|
|
734
|
+
initialDelaySeconds: 5
|
|
735
|
+
periodSeconds: 10
|
|
736
|
+
# Liveness probe: restart if the process is stuck
|
|
737
|
+
livenessProbe:
|
|
738
|
+
httpGet:
|
|
739
|
+
path: /health/live
|
|
740
|
+
port: 3000
|
|
741
|
+
initialDelaySeconds: 15
|
|
742
|
+
periodSeconds: 20
|
|
743
|
+
resources:
|
|
744
|
+
requests:
|
|
745
|
+
memory: "128Mi"
|
|
746
|
+
cpu: "100m"
|
|
747
|
+
limits:
|
|
748
|
+
memory: "256Mi"
|
|
749
|
+
cpu: "500m"
|
|
750
|
+
---
|
|
751
|
+
# HorizontalPodAutoscaler — scale based on CPU, not session count
|
|
752
|
+
apiVersion: autoscaling/v2
|
|
753
|
+
kind: HorizontalPodAutoscaler
|
|
754
|
+
metadata:
|
|
755
|
+
name: api-server-hpa
|
|
756
|
+
spec:
|
|
757
|
+
scaleTargetRef:
|
|
758
|
+
apiVersion: apps/v1
|
|
759
|
+
kind: Deployment
|
|
760
|
+
name: api-server
|
|
761
|
+
minReplicas: 2 # Minimum for high availability
|
|
762
|
+
maxReplicas: 20 # Scale up to 20 under load
|
|
763
|
+
metrics:
|
|
764
|
+
- type: Resource
|
|
765
|
+
resource:
|
|
766
|
+
name: cpu
|
|
767
|
+
target:
|
|
768
|
+
type: Utilization
|
|
769
|
+
averageUtilization: 70 # Scale at 70% CPU
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
## Cross-References
|
|
775
|
+
|
|
776
|
+
- **horizontal-vs-vertical:** Stateless design is the prerequisite for horizontal scaling. Without statelessness, horizontal scaling requires sticky sessions that negate most benefits. See `architecture/scaling/horizontal-vs-vertical.md`.
|
|
777
|
+
- **twelve-factor-app:** Factor VI (Stateless Processes) directly mandates this pattern. Factor IX (Disposability) depends on it. See `architecture/foundations/twelve-factor-app.md`.
|
|
778
|
+
- **serverless:** Serverless functions are stateless by definition — Lambda instances are ephemeral and interchangeable. Stateless design is not optional in serverless, it is enforced by the runtime. See `architecture/patterns/serverless.md`.
|
|
779
|
+
- **microservices:** Microservice instances must be interchangeable for Kubernetes scheduling, autoscaling, and failure recovery. Stateless design at the service level is a foundational requirement. See `architecture/patterns/microservices.md`.
|
|
780
|
+
- **caching-architecture:** The relationship between in-process caches and external caches is a core tension in stateless design. Caching strategies must account for process disposability and cold starts. See `architecture/scaling/caching-architecture.md`.
|
|
781
|
+
- **session-management:** Security implications of session externalization, cookie security, token storage, and session fixation attacks. See `security/web/session-management.md`.
|
|
782
|
+
- **authentication:** JWT vs. server-side sessions is fundamentally an authentication architecture decision with statelessness implications. See `security/foundations/authentication.md`.
|
|
783
|
+
- **event-driven:** Event-driven architectures complement stateless design by decoupling services through asynchronous messaging rather than shared state. See `architecture/patterns/event-driven.md`.
|
|
784
|
+
|
|
785
|
+
---
|
|
786
|
+
|
|
787
|
+
*Researched: 2026-03-08 | Sources: [The Twelve-Factor App — Processes](https://12factor.net/processes) | [Stateful vs Stateless Architecture: Why Stateless Won (Virtasant)](https://www.virtasant.com/blog/stateful-vs-stateless-architecture-why-stateless-won) | [Stop Using JWT for Authentication: The Stateless Myth (Deoxy)](https://deoxy.dev/blog/stop-using-jwt-for-auth/) | [JWT Revocation Strategies: When Stateless Tokens Need State (Michal Drozd)](https://www.michal-drozd.com/en/blog/jwt-revocation-strategies/) | [Session Management with Redis (Redis.io)](https://redis.io/solutions/session-management/) | [Microservice Session Management: From Stateful Pain to Stateless Gain with Redis and JWT (Medium)](https://medium.com/codetutorials/microservice-session-management-from-stateful-pain-to-stateless-gain-with-redis-and-jwt-72593a746b04) | [Stateful vs Stateless Architecture (GeeksforGeeks)](https://www.geeksforgeeks.org/system-design/stateful-vs-stateless-architecture/) | [The 12-Factor App — 15 Years Later: Does It Still Hold Up in 2026? (Medium)](https://lukasniessen.medium.com/the-12-factor-app-15-years-later-does-it-still-hold-up-in-2026-c8af494e8465) | [Netflix Microservices Architecture Case Study (Clustox)](https://www.clustox.com/blog/netflix-case-study/) | [Stateful vs Stateless Applications (Red Hat)](https://www.redhat.com/en/topics/cloud-native-apps/stateful-vs-stateless) | [Stateless Authentication: Understanding Token-Based Auth (Descope)](https://www.descope.com/learn/post/stateless-authentication) | [Why JWTs Make Terrible Authorization Tokens (DEV Community)](https://dev.to/stevenstuartm/why-jwts-make-terrible-authorization-tokens-3c8g)*
|