State and precedence

The tables the implementation is written against. Where this document and the code

disagree, the code is wrong — every rule here has a test named after it in

endpoint/tests/.

1. Precedence when deciding what governs a sign-in

Evaluated in this order. The first rule that applies decides.

#ConditionOutcomeTest
1Deployment trust or the signed license is missing, unverifiable, or the effective configuration is not usable on this buildBlock. The provider does not arm and setup fails. Never "carry on permissively".bootstrap/an_unusable_configuration_blocks_rather_than_permits
2The subject is a local account matching a pinned recovery SID, on the console, with recovery enabledLocal recovery exemption. Precedes block and MFA alike. Windows still checks the password.recovery/recovery_precedes_a_blocked_path
3The subject is any other local accountBlock. A local account is never offered to Core as a domain user.recovery/an_ordinary_local_account_is_not_a_recovery_account
4The channel's effective action is ignoreLeave to Windows. Coverage unprotected; no observation claimed.policy/ignore_reports_unprotected_and_log_reports_alternate_paths
5The channel's effective action is logObserve only. Windows authenticates; we record what we actually see.same
6The channel's effective action is blockBlock. Not a firewall, not an RDP listener shutdown.policy/block_is_refused_while_another_provider_could_complete_the_sign_in
7The scenario is not one this build supportsBlock. An unsupported scenario is not represented as intercepted.bootstrap/an_unusable_configuration_blocks_rather_than_permits
8Not yet activated, bootstrap_mode is enforce_until_activatedBlock. A deliberate lockout risk, overridden only by rule 2.bootstrap/enforce_until_activated_blocks_covered_sign_ins
9Not yet activated, bootstrap_mode is phone_home_firstAllow, basis bootstrap_inactive. A bypass, audited as one. Not an MFA success.bootstrap/phone_home_first_falls_through_to_windows_when_core_is_unreachable
10mode is pass_through and now < pass_through_untilAllow, basis tenant_pass_through. Administrative bypass with a deadline.grace/tenant_pass_through_applies_offline_and_expires
11OtherwiseIntercept. Run the Core-directed flow.—

An expired pass_through_until falls through to rule 11, not to unrestricted login.

2. Precedence when Core cannot be reached

Evaluated in order, only for an intercepted path.

#ConditionOutcome
1A known persistent denial applies to this device, user or associationDeny. An older grant is never consumed to override it.
2Not yet activated and phone_home_firstAllow, basis bootstrap_inactive
3Tenant pass-through still inside its deadlineAllow, basis tenant_pass_through
4A valid cached policy_exception lease covers this user and scenarioAllow, basis policy_exception_lease. Not grace, not counted against the grace budget.
5The transport failure is not an outage (TLS trust, refused redirect, oversized response, cancellation, missing credential)Deny. Section 8: a security or configuration error never becomes a grace-eligible timeout.
6A valid grace grant covers this user and scenario, the cause is eligible, and budget remainsAllow, basis offline_grace. Debited at release.
7OtherwiseDeny, with the specific reason (offline_exhausted, offline_expired, offline_quarantined, offline_clock_untrusted, offline_identity_ambiguous, …)

A completed exchange that returned 401, 403, 429 or any other protocol error takes none of

these branches: it is a decision from Core, or an authentication failure, and it denies.

3. Offline authorization refusals, in evaluation order

ConditionResultWhy
Durable state was missing or corruptquarantinedA wiped counter file is not a refilled budget
A clock rollback is latchedclock_untrustedExpiry cannot be judged honestly
No resolved SID for the accountidentity_ambiguousWe cannot tell whether a known denial applies
Grace disabled by the current policypolicy_disabled
This failure is not an eligible causecause_not_eligible
The grant does not cover this scenarioscenario_not_covered
A persistent denial appliesdenial_in_effect
Past min(grant.expires_at, issued_at + current max_age_seconds)expiredThe current ceiling caps an older grant
used >= min(grant.max_submissions, current max_submissions)exhaustedLikewise
The stored grant no longer verifiesgrant_invalidIts consumption record stays behind

4. Policy snapshot commit

Incoming versionPayloadResult
> acceptedvalid on this buildAccept. A higher version may deliberately relax policy: that is an authorized update, not a downgrade attack.
> acceptednot usable on this buildReject, configuration_rejected audited, previous snapshot stays in force
== acceptedbyte-identicalIdempotent no-op
== accepteddifferentConflict. Core must increment the version to change anything, including to clear a denial.
< acceptedanyConflict. A rollback, even validly signed.
anywrong tenant or wrong deviceBinding mismatch

5. Denials

Recorded when a decision denies with a scope wider than attempt. An otp_incorrect

denial is attempt-scoped and is forgotten with the attempt.

Cleared only when all of these hold:

An ordinary allow clears nothing. A policy refresh at the same version clears nothing. A

late allow from an older version cannot defeat a newer denial. Core is required to

increment the device policy revision when it lifts a restriction, precisely so this rule

can be strict.

6. The attempt state machine


  Idle ──BeginValidation──▶ Validating ──┬── require_sms ─────▶ WaitingForCode
                                         ├── allow_password_only ─▶ AuthorizedOnce
                                         └── deny ───────────────▶ Denied

  WaitingForCode ──BeginVerification──▶ Verifying ──┬── allow_mfa ──▶ AuthorizedOnce
                                                    ├── incorrect, attempts remain
                                                    │              ──▶ WaitingForCode
                                                    └── otherwise ──▶ Denied

  Idle | Validating ──AuthorizeOffline──▶ AuthorizedOnce
        (grace · exception lease · tenant pass-through · bootstrap · not intercepted)

  AuthorizedOnce ──BeginSubmission──▶ SubmittingToWindows ──▶ Completed

  any ──Cancel──▶ Cancelled     any ──Fail──▶ Failed
  any ──OnCredentialChanged──▶ Idle, new generation, fresh nonce required

Invariants, each with a test:

answer to this attempt is the verification result.

fresh authorization.

7. Authorization bases, and what each may be called

BasisIs it MFA?Audit bypass_kind
mfa_verifiedYes — the only onenone
online_password_onlyNopolicy_exception
policy_exception_leaseNopolicy_exception
tenant_pass_throughNotenant_passthrough
offline_graceNograce
local_recoveryNolocal_recovery
bootstrap_inactiveNobootstrap_inactive
scenario_not_interceptedNoscenario_unprotected

IsMfaSuccess() returns true for exactly one value. No report can aggregate an exception

with a verification, because the two never share a code path.

8. Trusted time

SourcePropertyUsed for
Wall clockSettable by any administratorThe boot anchor only
Monotonic (unbiased interrupt time)Cannot decrease within a bootAdvancing from the anchor
Persisted high-water markOnly ever moves forwardThe floor under every expiry check

Now() is anchor_wall + monotonic_elapsed, never below the high-water mark. The wall

clock is read once, at construction: changing the system time mid-session moves nothing.

A wall clock more than 900 seconds behind the high-water mark latches

rollback_detected, which refuses all time-based offline authorization until a verified

server timestamp clears it. Ordinary NTP corrections and a VM resuming with a stale RTC

sit inside that tolerance.

What this does not do: it is not rollback resistance against an administrator who

restores a disk image or a VM snapshot. That would need a hardware or server mechanism

this MVP does not have, and the proposal says so in those words.