---
id: WF-PFM-OPS-016
title: Carry a licensing change into access decisions
product_area: platform
module: operations
revision: 1
status: draft
design_intent: confirmed
implementation_evidence: observed
inference: []
origin: 'services-platform:docs/licensing.md#2. What this does NOT do (Andrew, 2026-10-13: "enforce everywhere")'
trigger: 'A completed licensing flow reaches cache refresh, or a successful company replacement changes windows.'
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
implements:
  - CAP-PFM-OPS-014
  - CAP-PFM-OPS-011
  - CAP-PFM-OPS-012
  - CAP-PFM-OPS-015
moves: []
crosses:
  - SVC-PFM-OPS-licensing
idempotency: >-
  Each refresh request has a fresh job submission identity. Repeated invalidation is safe rather than
  deduplicated into one licensing decision.
concurrency: >-
  Refresh jobs run independently. A new authorization decision reads current committed data, but an existing
  cached answer can remain valid until refresh or expiry.
hops:
  - 'n': 1
    actor_or_service: Licensing
    action: >-
      Request Security’s authorizer-refresh job after the licensing write and preceding follow-up steps
      complete.
    anchor: >-
      services-platform:modules/security/services/authorizer-flush/submit-flush-job.ts:submitAuthorizerFlushJob
    seam: state_machine
    on_failure: Contain and log refresh submission errors.
    left_inconsistent: Cached authority retains the older answer until expiry or later invalidation.
    observer: Refresh-job monitoring and submission logs.
    provenance: observed
    source: >-
      services-platform:modules/security/services/authorizer-flush/submit-flush-job.ts:submitAuthorizerFlushJob
  - 'n': 2
    actor_or_service: Security refresh job
    action: Run the Security-owned job to invalidate gateway authorizer cache.
    anchor: 'services-platform:modules/security/services/authorizer-flush/authorizer-flush-workflow.ts'
    seam: state_machine
    on_failure: Use that job’s workflow retry and reconciliation behavior.
    left_inconsistent: Gateway cache can retain its earlier decision.
    observer: Job page is the observation surface.
    provenance: documented
    source: 'services-platform:docs/licensing.md#9. How a licence reaches the permission check'
  - 'n': 3
    actor_or_service: Security
    action: Resolve available applications across reachable workspaces and sign the decision into request context.
    anchor: 'services-platform:modules/security/services/persistence/assignments.ts:resolveGrantSlice'
    seam: sync
    on_failure: Refuse the gateway request when decision construction fails.
    left_inconsistent: No newly signed successful decision is returned.
    observer: Authorization decision logs expose the result.
    provenance: documented
    source: 'services-platform:docs/licensing.md#9. How a licence reaches the permission check'
  - 'n': 4
    actor_or_service: Request dispatcher
    action: >-
      When configured to enforce, reject an unavailable application before its handler, except the declared
      exempt applications.
    anchor: 'services-platform:aws/platform.names.ts:PLATFORM_LICENSING_MODE'
    seam: sync
    on_failure: Return the intended application refusal rather than execute the business handler.
    left_inconsistent: No business handler effect results from that refusal.
    observer: Refused request and authorization logs is the observation surface.
    provenance: documented
    source: 'services-platform:docs/licensing.md#9. How a licence reaches the permission check'
branches:
  - condition: A function deployed without the mode variable
    path: 'When no mode is configured, application enforcement defaults off and does not refuse requests.'
  - condition: A grant or window ends on the clock
    path: Time-based expiry submits no refresh; cached authorization catches up on cache expiry.
waits:
  - what: Authorizer cache
    how_long: sixty seconds plus the signed context’s thirty seconds at most
evidence:
  - >-
    services-platform at ad3f6a6: modules/security/services/authorizer-flush/submit-flush-job.ts;
    aws/platform.names.ts (PLATFORM_LICENSING_MODE enforce); aws/cdk/platform.function-environment.ts;
    docs/licensing.md §9.
  - >-
    codex examination 2026-10-23: checked the pinned source anchors, planning references and enforcement
    boundaries; unresolved limits are recorded in module findings.
controls: [TC-AVL-001, TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CHG-005, TC-CHG-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-DAT-005, TC-DAT-006, TC-IAM-001, TC-IAM-002, TC-IAM-003, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-MON-003, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Carry a licensing change into access decisions

## What It Does

Licensing requests Security’s cache-refresh job after successful follow-up work. Later requests receive newly resolved availability or reuse a bounded cached answer. The dispatcher applies availability in enforce mode and exempts Platform, Rhythm HQ and Contacts & Organizations.

## The Flow

```mermaid
flowchart TD
  H0["Request Security’s authorizer-refresh job after the licensing write and preceding follow-up steps complete."]
  H1["Run the Security-owned job to invalidate gateway authorizer cache."]
  H0 --> H1
  H2["Resolve available applications across reachable workspaces and sign the decision into request context."]
  H1 --> H2
  H3["When configured to enforce, reject an unavailable application before its handler, except the declared exempt applications."]
  H2 --> H3
```

## When Something Fails

An omitted or unsuccessful refresh leaves cache expiry as fallback. Override history failure can prevent the request from reaching refresh. Clock-based expiry submits no refresh. The pinned deployment guide records enforcement enabled, but a function lacking the setting uses its permissive default. The ordinary cache-and-context allowance is about ninety seconds rather than an instantaneous guarantee.
