# Business Rules — Unit 2: slave-availability-extension ## Rule Set 1: API Key Validation ```mermaid graph TD A{Registration\nexists in DB?} -->|No| B{Is this a\nregister call?} B -->|Yes| C[Accept and create\nnew registration] B -->|No| D[Return 401\nUnauthorized] A -->|Yes| E{X-Master-Api-Key\nmatches stored key?} E -->|Yes| F[Proceed with\nbusiness logic] E -->|No| G[Return 401\nUnauthorized] classDef decision fill:#FFC107,stroke:#F57F17,color:#000 classDef pass fill:#9ae6b4,stroke:#2f855a,color:#000 classDef fail fill:#FC8181,stroke:#C53030,color:#000 class A,B,E decision class C,F pass class D,G fail ``` Text alternative: If no registration exists and this is a register call, create it. If no registration and not a register call, 401. If registration exists, validate key; match = proceed, mismatch = 401. **Validation rules**: | # | Rule | Applies to | |---|------|-----------| | BR-SLAVE-01 | First `POST /register` with no existing registration: accept unconditionally, store `ApiKey` from `X-Master-Api-Key` header | Register endpoint | | BR-SLAVE-02 | Subsequent `POST /register`: validate header against stored `ApiKey`. Match → update `MasterUrl` + `LastContactedAt`. Mismatch → 401. | Register endpoint | | BR-SLAVE-03 | `POST /status` without existing registration → 401 | Status push endpoint | | BR-SLAVE-04 | `POST /status` with key mismatch → 401 | Status push endpoint | | BR-SLAVE-05 | `GET /registered-url` without existing registration → 401 | Get-URL endpoint | | BR-SLAVE-06 | `GET /registered-url` with key mismatch → 401 | Get-URL endpoint | | BR-SLAVE-07 | Missing or empty `X-Master-Api-Key` header → 401 on all endpoints | All master endpoints | --- ## Rule Set 2: Master Gate Bypass ```mermaid graph TD A{Path starts with\nbypass prefix?} -->|Yes| B[Pass through\nunconditionally] A -->|No| C{Valid admin\nJWT bearer?} C -->|Yes| B C -->|No| D{Master gate\nenabled?} D -->|_masterIsAvailable = true\nor default| E[Proceed to\nlocal gate] D -->|_masterIsAvailable = false| F[Return 503\nwith master message] E --> G{Local availability\ncheck} G -->|Available| H[Pass to next\nmiddleware] G -->|Unavailable| I[Return 503\nwith local message] classDef decision fill:#FFC107,stroke:#F57F17,color:#000 classDef pass fill:#9ae6b4,stroke:#2f855a,color:#000 classDef fail fill:#FC8181,stroke:#C53030,color:#000 class A,C,D,G decision class B,E,H pass class F,I fail ``` Text alternative: Bypass path check first. Admin JWT next (bypasses both gates). Then master gate (static field). If master blocks: 503. If master passes: local gate. If local blocks: 503. Otherwise pass through. **Bypass prefix list** (extended from existing): | Path prefix | Reason | |-------------|--------| | `/api/v1/Availability/status` | Already bypassed — public status endpoint | | `/api/v1/Auth/` | Already bypassed — login must always work | | `/api/v1/Setup/status` | Already bypassed — frontend init check | | `/api/v1/master/` | **NEW** — master management endpoints must bypass gate so master can always push status or re-register | **Cache behavior rules**: | # | Rule | |---|------| | BR-SLAVE-08 | `_masterIsAvailable` defaults to `true` (fail-open) on process startup | | BR-SLAVE-09 | `_masterDisableMessage` defaults to `null` on process startup | | BR-SLAVE-10 | Cache has no expiry (Q4=A); only updated on `POST /status` with valid API key | | BR-SLAVE-11 | 503 response from master gate includes `_masterDisableMessage` in `ProblemDetails.Detail` | --- ## Rule Set 3: MasterRegistration Singleton | # | Rule | |---|------| | BR-SLAVE-12 | `MasterRegistration` is a singleton: `Id` is always `Guid.Parse("00000000-0000-0000-0000-000000000001")` | | BR-SLAVE-13 | On `RegisterAsync`: if row with that Id exists → update. If not → insert. Never delete. | | BR-SLAVE-14 | `RegisteredAt` is set once at creation and never updated | | BR-SLAVE-15 | `LastContactedAt` is updated on every successful master call (register, status push, get-url) | --- ## Rule Set 4: Controller Routing | Endpoint | Method | Route | Auth | |----------|--------|-------|------| | Register master | `POST` | `/api/v1/master/register` | None (API key in header) | | Receive status push | `POST` | `/api/v1/master/status` | None (API key in header) | | Get registered URL | `GET` | `/api/v1/master/registered-url` | None (API key in header) | All three endpoints are unauthenticated from ASP.NET Core's perspective — they use the custom `X-Master-Api-Key` header validation implemented in `MasterAvailabilityService`. They are also in the middleware bypass list so the gate cannot block master management calls.