Hacker DirectoryHacker Directory

WEB / WEB-12.04

Client and server state ownership

A note can be visible in the Browser, saved on the server, or still queued. State ownership determines which copy can make which decision.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Base version matches

  1. Draft not sent yet

    The Browser makes a new note from a v3 snapshot; the server record remains v3.

    Browser / Duru
    Base: v3; draft unsaved
    Application
    No request
    Authoritative record
    Current: v3
  2. Update request arrived

    The Application receives new text and If-Match v3. Request arrival does not yet mean acceptance.

    Browser / Duru
    Base: v3; waiting
    Application
    Received: If-Match v3; checks pending
    Authoritative record
    Current: v3
  3. Trusted checks pass

    The supplied authenticated context, authorization, and input are valid. No mutation has occurred.

    Browser / Duru
    Base: v3; waiting
    Application
    Trusted checks passed; conditional commit pending
    Authoritative record
    Current: v3
  4. Conditional local commit

    Current v3 matches; check and write produce v4 at one boundary. This is a logical in-process operation, without a wire.

    Browser / Duru
    Base: v3; waiting
    Application
    Committed result: v4
    Authoritative record
    Current: v4
  5. Successful response arrived

    The Browser learns the new version and only now displays a server-save state.

    Browser / Duru
    Known: v4; saved on server
    Application
    Response: 200, ETag v4
    Authoritative record
    Current: v4

Stale precondition

  1. Browser uses an old snapshot

    The new draft is based on v2; the trusted record is v3.

    Browser / Duru
    Base: v2; draft unsaved
    Application
    No request
    Authoritative record
    Current: v3
  2. Old validator arrived

    The Application receives If-Match v2; this field is not authorization proof.

    Browser / Duru
    Base: v2; waiting
    Application
    Received: If-Match v2; checks pending
    Authoritative record
    Current: v3
  3. Precondition false

    Even with valid trusted checks, v2 and v3 do not match. The fixture does not mutate the record.

    Browser / Duru
    Base: v2; waiting
    Application
    Rejected: precondition false
    Authoritative record
    Current: v3; unchanged
  4. Conflict response arrived

    412 reaches the Browser. The draft is preserved; the server record did not change.

    Browser / Duru
    Conflict; draft preserved
    Application
    Response: 412
    Authoritative record
    Current: v3; unchanged
  5. Reconciliation is needed

    The UI offers to read the current record and compare the draft; it does not force an overwrite.

    Browser / Duru
    Conflict; current record needed
    Application
    Response: 412
    Authoritative record
    Current: v3; unchanged

Commit · response lost

  1. Snapshot and draft

    The Browser holds a v3 snapshot and an unsaved draft. No request has been sent; the server’s authoritative record is still v3.

    Browser / Duru
    Base: v3; draft unsaved
    Application
    No request
    Authoritative record
    Current: v3
  2. Request reached the Application

    The update request arrived; the Browser waits for the result.

    Browser / Duru
    Base: v3; waiting
    Application
    Received: If-Match v3
    Authoritative record
    Current: v3
  3. Trusted conditional commit

    Supplied trusted checks and the v3 precondition pass; the record becomes v4. This is a local operation.

    Browser / Duru
    Base: v3; waiting
    Application
    Committed: v4; response prepared
    Authoritative record
    Current: v4
  4. Response did not arrive

    In this authored branch, the connection fails after commit. There is no completed arrival, so no transfer to the Browser is drawn.

    Browser / Duru
    Known: v3; no response
    Application
    Response delivery failed after commit
    Authoritative record
    Current: v4
  5. Client outcome unknown

    The Browser preserves the draft and needs to reobserve state. Server v4 is known to the fixture but not yet to the Browser.

    Browser / Duru
    Outcome unknown; draft preserved
    Application
    Response delivery failed after commit
    Authoritative record
    Current: v4

ACTORS · STATE · DATA FLOW

A note can have several valid states

Duru writes a note for a book in Atlas. Seeing “new note” in the Browser input does not mean the server record changed. The UI owns the first copy; the trusted server side of the application owns the canonical business record.

Client state does not have to be temporary; it can remain in IndexedDB. Server state does not have to mean a database; it can reside in process memory or another store. The distinction comes from who accepts a change and which decision the state authoritatively controls, not the storage product name.

In this example, UI preferences and an unsent draft belong to the Browser. The accepted note text, resource ownership, and Atlas membership are determined on the server. Collapsing them into one “saved” label creates false confidence.

Separate a snapshot from authority

The Browser can keep text and a version received from the server. This is a snapshot observed at a particular time. If another tab or worker updates the note, the Browser’s older copy does not become authoritative by itself.

The server should also treat tenant, owner, or role fields in the request body as user claims. The request’s authenticated Duru context and the target note’s ownership within Atlas are resolved through trusted records. The client can choose a resource ID; it cannot choose the right to act on that resource.

Application and Authoritative record are drawn as separate actors, but this lesson models them as logical components within the same server process. Their commit is not an HTTP request; the following session-store lesson depicts a remote store differently.

Normal save: make an accepted update visible

Initially, the server has note-v3 and the Browser knows a snapshot of the same version. Duru’s new text is based on that snapshot. The request contains the new text and an If-Match precondition for the strong ETag “note-v3”.

The Application first applies authenticated context, resource authorization, and input rules. It then evaluates the precondition and update within one consistent commit boundary. If the record is still v3 in this fixture, the new text is accepted as v4. The response reports the new representation version.

Only after processing the successful response can the Browser say “saved on the server”. Sending a request expresses intent, server commit is a decision, and response arrival lets the Browser learn that decision. Keeping these states separate makes connection failures understandable.

Stale snapshots: do not silently overwrite newer work

In the second branch, the Browser writes from v2 while the server’s current note is v3. An unconditional overwrite could erase a change already accepted elsewhere. If-Match uses strong comparison for the current representation to help prevent this lost update.

The fixture does not apply the mutation when the precondition is false and returns 412 Precondition Failed. This is not a login failure; the representation the Browser relied on does not match current state. The server remains v3, the draft is preserved, and the UI shows a conflict.

The client should not merely obtain a newer ETag and automatically force the old draft over it. Fetch the current note, compare the user’s intent with the current text, and, when appropriate, build a newly approved update on the new base version. Separately racing check and write operations defeat the intended protection.

A lost response can leave the outcome unknown

After the server commits v4, the response might not reach the Browser. With only v3 known, the Browser has no basis to say either “the server did not save it” or, merely because a request was sent, “saved”. In this branch, server state changed while client knowledge is unknown.

Recovery means rereading the current representation and reconciling it with the draft, rather than treating a lost connection as proof of failed mutation. The PUT method in this example is idempotent under HTTP semantics: repeating the same request has the same intended effect, although the response can differ. With the fixture’s strict If-Match policy, v3 no longer matches after the first commit. Other operations need their own semantics and idempotency design for safe retries.

Keep the same distinction for an offline queue. “Queued locally” is useful success for a user, but it is not a promise of server acceptance. A queued update is validated again when it reaches the server because both permission and state may have changed.

What does an ETag establish?

An ETag is an opaque representation validator, not a credential, role, or ownership proof. Here, note-v3 is a readable instructional alias. The application chooses its validator format; any field that resembles an increasing number is not automatically a strong ETag.

If-Match carries a concurrency condition. Authorization answers whether Duru may perform this action on this note. When both are needed, both are enforced. Knowing the same version does not grant permission, and having permission does not require unconditional overwrites of stale state.

2. Red · Trust & Impact

Read every scenario without motion

Matching snapshot

  1. The update intent is in the browser

    Duru’s draft is based on snapshot note-v3. The PUT prepares If-Match="note-v3"; new text in the browser does not show that the server record changed.

    Browser draft
    Draft; base="note-v3"
    Application
    Update not received
    Server record
    Server="note-v3"
  2. The conditional update arrived

    The application received PUT /books/book-17/notes/mine. Duru/Atlas authorization is supplied as already allowed in this fixture; no new permission is derived from the header.

    Browser draft
    Awaiting outcome
    Application
    If-Match="note-v3" received
    Server record
    Server="note-v3"
  3. The version condition was separated

    If-Match is a concurrency condition; an ETag is neither a credential nor ownership proof. Application identity, input and resource controls are separate from this condition.

    Browser draft
    Awaiting outcome
    Application
    Base note-v3; current note-v3
    Server record
    Server="note-v3"
  4. The condition was applied at the commit boundary

    Comparison and mutation share one consistent boundary. Matching base and current versions allow the new note to commit as note-v4. Application and record are logical components in the same process here.

    Browser draft
    Awaiting outcome
    Application
    precondition=true
    Server record
    Server="note-v4"
  5. The HTTP outcome was prepared

    The application prepares a 200 response. The browser has not yet learned the successful commit.

    Browser draft
    Awaiting outcome
    Application
    200 response ready
    Server record
    Server="note-v4"
  6. The server outcome reached the browser

    After receiving the response, the browser knows the server-saved outcome and note-v4 snapshot.

    Browser draft
    Saved; note-v4
    Application
    200 response ready
    Server record
    Server="note-v4"

Stale snapshot

  1. The update intent is in the browser

    Duru’s draft is based on snapshot note-v2. The PUT prepares If-Match="note-v2"; new text in the browser does not show that the server record changed.

    Browser draft
    Draft; base="note-v2"
    Application
    Update not received
    Server record
    Server="note-v3"
  2. The conditional update arrived

    The application received PUT /books/book-17/notes/mine. Duru/Atlas authorization is supplied as already allowed in this fixture; no new permission is derived from the header.

    Browser draft
    Awaiting outcome
    Application
    If-Match="note-v2" received
    Server record
    Server="note-v3"
  3. The version condition was separated

    If-Match is a concurrency condition; an ETag is neither a credential nor ownership proof. Application identity, input and resource controls are separate from this condition.

    Browser draft
    Awaiting outcome
    Application
    Base note-v2; current note-v3
    Server record
    Server="note-v3"
  4. The condition was applied at the commit boundary

    Comparison and mutation share one consistent boundary. Base v2 does not match current v3, so no mutation occurs and note-v3 remains. Application and record are logical components in the same process here.

    Browser draft
    Awaiting outcome
    Application
    precondition=false
    Server record
    Server="note-v3"
  5. The HTTP outcome was prepared

    The application prepares a 412 response. 412 is a concurrency conflict, not failed login, and does not change the preserved draft.

    Browser draft
    Awaiting outcome
    Application
    412 response ready
    Server record
    Server="note-v3"
  6. The server outcome reached the browser

    After receiving the response, the browser knows the conflict; the old snapshot and draft remain. Old text is not blindly forced through with a new ETag.

    Browser draft
    Conflict; draft preserved
    Application
    412 response ready
    Server record
    Server="note-v3"

ASSUMPTION · BOUNDARY · CONTROL

Which state feeds the decision?

In a defensive review, distinguish a visible UI button from the server accepting its action. A hidden/disabled control is interaction state only. Seeing role=editor in the Browser is not a trusted authorization result.

When a workflow receives a tenant or owner selection from the client, the server must resolve the relationship between authenticated subject and resource through its own trusted data. This fixture supplies an already validated Duru/Atlas context; it is not a simulated login flow or permission test.

A success label is also a claim

When reviewing a save flow, inspect when optimistic UI claims “saved”. If rejection or an unknown outcome does not correct that claim, the product misleads the user about state.

Three inert branches suffice for inspection: a matching base version, a stale base version, and response loss after commit. In the last branch, two copies carrying different knowledge is normal; it does not imply authentication compromise.

For retry review, preserving the same intended effect and receiving the same response are separate questions. If the first PUT committed here, another attempt with the old If-Match can receive 412 under the selected strict policy; that does not undo the earlier commit. Rereading a representation establishes state observed at that time. The missing response alone cannot reveal whether another update intervened, so the UI must preserve the corresponding pending uncertainty.

3. Blue · Secure development & defense

Read every scenario without motion

Response observed

  1. The update evidence scope was selected

    Review uses the normal row. Server decision and browser knowledge are separate fields; they need not have the same snapshot.

    Update row
    case=normal
    Commit review
    Not compared
    Client knowledge
    Client outcome unknown
  2. The precondition result was read

    browserBase and serverBefore are note-v3; precondition=true describes this update’s version condition. It is not authentication or resource-permission evidence.

    Update row
    case=normal
    Commit review
    precondition=true
    Client knowledge
    Client outcome unknown
  3. The server commit was checked separately

    The row records serverDecision=committed and serverAfter=note-v4. A 200 response was prepared; that status alone does not establish response receipt.

    Update row
    case=normal
    Commit review
    Committed; note-v4; status=200
    Client knowledge
    Client outcome unknown
  4. Receipt information was compared

    With responseObserved=true, browserKnowledge is note-v4 and the UI is saved on server.

    Update row
    case=normal
    Commit review
    Server: note-v4
    Client knowledge
    Browser: note-v4; saved
  5. The integrity selection was bounded

    The selector counting false preconditions with true commits returns 0 for the main fixture and 1 for its separate positive fixture. Missing response receipt does not violate that predicate.

    Update row
    case=normal
    Commit review
    Main count=0; positive=1
    Client knowledge
    Receipt: true
  6. The client claim was tied to evidence

    This row shows both commit and receipt, supporting the server-saved claim. It says nothing about later updates.

    Update row
    case=normal
    Commit review
    Commit evidence present
    Client knowledge
    Receipt evidence present

Missing response

  1. The update evidence scope was selected

    Review uses the response-lost row. Server decision and browser knowledge are separate fields; they need not have the same snapshot.

    Update row
    case=response-lost
    Commit review
    Not compared
    Client knowledge
    Client outcome unknown
  2. The precondition result was read

    browserBase and serverBefore are note-v3; precondition=true describes this update’s version condition. It is not authentication or resource-permission evidence.

    Update row
    case=response-lost
    Commit review
    precondition=true
    Client knowledge
    Client outcome unknown
  3. The server commit was checked separately

    The row records serverDecision=committed and serverAfter=note-v4. A 200 response was prepared; that status alone does not establish response receipt.

    Update row
    case=response-lost
    Commit review
    Committed; note-v4; status=200
    Client knowledge
    Client outcome unknown
  4. Receipt information was compared

    With responseObserved=false, browserKnowledge remains the old note-v3 snapshot with outcome unknown. This does not support concluding that no commit occurred.

    Update row
    case=response-lost
    Commit review
    Server: note-v4
    Client knowledge
    Browser: note-v3; UNKNOWN
  5. The integrity selection was bounded

    The selector counting false preconditions with true commits returns 0 for the main fixture and 1 for its separate positive fixture. Missing response receipt does not violate that predicate.

    Update row
    case=response-lost
    Commit review
    Main count=0; positive=1
    Client knowledge
    Receipt: false
  6. The client claim was tied to evidence

    This row shows a server commit while the client outcome is unknown. Preserve the draft and reread the current representation for reconciliation before asserting either saved or not saved.

    Update row
    case=response-lost
    Commit review
    Commit evidence present
    Client knowledge
    UNKNOWN: client confirmation

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Write down ownership and success boundaries

Define the Browser as owner of UI input and local drafts, and the trusted server as owner of accepted notes and access policy. Track each state’s source, version, and freshness. When using a cache or snapshot, identify where the authoritative record resides.

Use distinct UI states such as unsaved, saving, saved locally, queued, saved on server, and conflict where they matter. Do not invent success or failure for an outcome the state model does not know.

Enforce the condition together with commit

For an endpoint accepting concurrent updates, enforce request authorization and input validation, then perform the precondition comparison and mutation at a consistent commit boundary. Choosing a database conditional update, transaction, or another appropriate primitive depends on the implementation.

A stale precondition should leave the current record unchanged. Provide a conflict response and reconciliation flow that preserves the draft. Checking an ETag only in the frontend does not protect the server’s lost-update boundary.

Reobserve an unknown response outcome

After a connection failure, reread the server record and compare it with the known version and draft. If the mutation needs retrying, use the operation’s idempotency and concurrency policy. Explain that the draft is preserved and what remains pending while the server outcome is confirmed.

Nonsecret observations can use baseVersion, currentVersion, decision, and responseObserved. These do not constitute a real HTTP capture; the fixture shows its own state transitions. The boundary is understandable without logging the user’s note text or session credential.

NOW APPLY IT TO THE CASE

When does the browser know that the server changed?

If-Match is only a version condition in this note update. Server commit and browser learning the response are shown as distinct boundaries.

Which owner holds the case’s browser data? Explain why presence or deletion of a client value differs from the server-side session outcome.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Note-update ownership and version ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "resource": "atlas/book-17/note-duru",
  "authenticatedContext": {
    "displayName": "Duru",
    "tenant": "Atlas",
    "authorization": "supplied as already allowed; not verified by this fixture"
  },
  "requestShape": {
    "method": "PUT",
    "path": "/books/book-17/notes/mine",
    "ifMatch": "\"note-v3\"",
    "bodyClass": "harmless new reading note"
  },
  "cases": [
    {
      "case": "normal",
      "browserBase": "\"note-v3\"",
      "serverBefore": "\"note-v3\"",
      "precondition": true,
      "serverAfter": "\"note-v4\"",
      "serverDecision": "committed",
      "responseStatus": 200,
      "responseObserved": true,
      "browserKnowledge": "\"note-v4\"",
      "ui": "saved on server"
    },
    {
      "case": "stale",
      "browserBase": "\"note-v2\"",
      "serverBefore": "\"note-v3\"",
      "precondition": false,
      "serverAfter": "\"note-v3\"",
      "serverDecision": "not committed",
      "responseStatus": 412,
      "responseObserved": true,
      "browserKnowledge": "old snapshot plus conflict",
      "ui": "conflict; draft preserved"
    },
    {
      "case": "response-lost",
      "browserBase": "\"note-v3\"",
      "serverBefore": "\"note-v3\"",
      "precondition": true,
      "serverAfter": "\"note-v4\"",
      "serverDecision": "committed",
      "responseStatus": 200,
      "responseObserved": false,
      "browserKnowledge": "\"note-v3\"; outcome unknown",
      "ui": "pending confirmation; draft preserved"
    }
  ],
  "selector": {
    "xml": "<updates><update case=\"normal\" precondition=\"true\" committed=\"true\" responseObserved=\"true\"/><update case=\"stale\" precondition=\"false\" committed=\"false\" responseObserved=\"true\"/><update case=\"response-lost\" precondition=\"true\" committed=\"true\" responseObserved=\"false\"/></updates>",
    "xpath": "count(/updates/update[@precondition=\"false\" and @committed=\"true\"])",
    "expected": 0,
    "positiveFixtureXml": "<updates><update precondition=\"false\" committed=\"true\"/></updates>",
    "positiveExpected": 1
  }
}
browserBase / serverBefore
The Browser snapshot validator and the server current validator; equality is only a concurrency precondition.
serverAfter / serverDecision
The trusted server commit result, independent of whether the response arrived.
responseObserved / browserKnowledge
Whether the Browser learned the outcome; sending alone is not proof of a server save.
selector
Selects fixture records that simultaneously claim a false precondition and a commit.

ETags are instructional aliases and response codes are chosen fixture behavior. The store is a logical in-process record. Real authorization, an HTTP server, concurrency races, and network failure were not executed.

Two reality checks

A value stored or displayed in the Browser is authoritative state for the server.

A client copy can be input or a snapshot. Resource ownership, authenticated authority, and accepted records are resolved on the trusted server.

Without a response, the server update certainly failed; resending is always safe.

A commit may have happened before the response was lost. For an unknown outcome, observe current state and reconcile using the operation’s concurrency/idempotency rules.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

The browser sends PUT using note-v2 while the server has note-v3. Does the selected state change?

  1. No; 412, the server retains note-v3 and the draft is preserved.
  2. Yes; the client draft overrides current server state.
Read the explained answer

No; 412, the server retains note-v3 and the draft is preserved. — The stale precondition is false; the selected no-commit branch need not discard the user draft.

What saved claim can the browser establish in the response-lost branch?

Selected lesson artifact fields; teaching example

cases[2].serverDecision
committed
cases[2].responseObserved
false
cases[2].browserKnowledge
"note-v3"; outcome unknown
cases[2].ui
pending confirmation; draft preserved
  1. If the request failed, the server definitely performed no write.
  2. Outcome is unknown; pending confirmation and draft preservation are appropriate.
Read the explained answer

Outcome is unknown; pending confirmation and draft preservation are appropriate. — The global fixture supplies a server commit but responseObserved=false; browserKnowledge does not contain it.

Explain the three ownership boundaries of a client draft, a current server record and supplied authorization.

Read the explained answer

A client draft is content the browser has not yet had accepted by the server. The server record carries the current revision and committed value; If-Match distinguishes stale state. AuthenticatedContext permission is supplied as already allowed here, not a verified authentication test. Revision matching does not replace tenant/user permission.

□ I distinguished the draft from the current record.

□ I did not count supplied permission as native authentication proof.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope