# ADR-109: Single Infrastructure, Feature-Gated Tiers

**Date:** 2026-03-16 | **Status:** ACCEPTED

## Context

The question arose: should free users and paid users run on separate infrastructure? The user (product owner) explicitly decided: **"No two infrastructure. For free users also we will do the same but gated features."**

This aligns with the SaaS best practice of reducing operational complexity. Running separate backends for different tiers creates:
- Double the deployment pipelines
- Double the monitoring
- Data isolation complexity
- Higher cost for the same outcome

## Decision

**One infrastructure stack serves all tiers.** Gating happens at the application layer, not the infrastructure layer.

### Stack (Single)

```
All users → graqle.com → Amplify SSR → Lambda Function URL → Neptune
```

### Gating Implementation

```typescript
// Frontend: TierGate component
<TierGate feature="traversal_native" fallback={<BlurredPreview />}>
  <TraversalResults />
</TierGate>

// Backend: license check middleware
@require_feature("traversal_native")
async def traversal_query(request):
    ...
```

### User Tiers (from Cognito custom attributes)

| Attribute | Values | Source |
|-----------|--------|--------|
| `custom:tier` | `free`, `pro`, `team` | Set on signup (free), Stripe webhook (pro/team) |
| `custom:project_limit` | `1`, `3`, `unlimited` | Derived from tier |
| `custom:org_id` | UUID or null | Set for Team tier |

### What Changes Per Tier

| Layer | Free | Pro | Team |
|-------|------|-----|------|
| **Neptune** | Same cluster, same queries | Same | Same |
| **Lambda** | Same function, same code | Same | Same |
| **Amplify** | Same frontend | Same | Same |
| **Feature gates** | 500 nodes, basic viz, 3 queries/mo | Full viz, 100 queries/mo | + cross-project, + team |

### Public Access (No Sign-Up)

Users who haven't signed up can see the base layer:
- Landing page with demo graph
- Graph Explorer with Graqle's own KG (read-only)
- No save, no reasoning, no intelligence dashboards
- Call to action: "Sign up free to scan your own project"

This is implemented by making the graph visualization endpoint public (no auth required) while gating write operations and advanced features behind auth.

## Consequences

### Positive
- One deployment pipeline (CI/CD is already set up)
- One Neptune cluster to manage
- One Lambda function to monitor
- Easier debugging (same code path for all users)
- Lower cost (no idle infrastructure for low-traffic tiers)

### Negative
- Noisy neighbor risk (a Team user's heavy query could affect a Free user). Mitigate with request queuing.
- Feature gate logic must be rigorous (accidental access to Pro features is a revenue leak)
- Cognito custom attributes must be set correctly on every tier change

### Implementation Priority
1. Graph viz is already public (Function URL, no auth)
2. Add `custom:tier` to Cognito user pool
3. Add `TierGate` component to frontend
4. Add `@require_feature` decorator to backend routes
5. Wire Stripe webhooks to update `custom:tier`
