Skip to content

ETag Concurrency

CEDAR protects mutable REST resources with standard HTTP ETags and If-Match. This is optimistic concurrency control: a client may prepare an edit without holding a lock, but the server accepts the write only if the resource has not changed since that client read it.

Every successful create, single-resource read, and update of a revisioned resource returns a strong ETag response header. For example, a newly created artifact normally starts with:

ETag: "1"

The tag is the version of the representation returned by that request. CEDAR currently bases it on a server-owned revision that advances atomically after each successful change. The quotes are part of the header value. Even when a tag looks like a number, treat the complete value as opaque: save what the server returned rather than parsing it, incrementing it, or constructing a replacement.

Read, Edit, and Write the Same Revision

First retrieve the current artifact and retain both its body and its ETag:

curl -i "$CEDAR_API/templates/<URL_ENCODED_TEMPLATE_ID>" \
  -H "Authorization: apiKey $CEDAR_API_KEY" \
  -H "Accept: application/json"

If that response contains ETag: "7", send that exact value when replacing or deleting the artifact. A conditional update looks like this:

curl -i -X PUT "$CEDAR_API/templates/<URL_ENCODED_TEMPLATE_ID>" \
  -H "Authorization: apiKey $CEDAR_API_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "7"' \
  --data-binary @updated-template.json

A successful response returns the replacement body and its new tag, for example ETag: "8". Keep that returned value for the next change. The complete sequence is therefore:

POST creates the artifact                -> ETag: "1"
GET reads the current artifact           -> ETag: "1"
PUT with If-Match: "1" succeeds         -> ETag: "2"
another PUT with the old If-Match: "1"  -> 412 Precondition Failed

The server performs the revision comparison and the write atomically. Two clients can both read "7", but only the first write can replace revision 7. The second receives 412 Precondition Failed rather than silently overwriting the first client's work.

Some PUT routes can either create a resource at a client-supplied identifier or replace the resource already there. Omit If-Match only when creating at an identifier that does not yet exist; if it exists, replacement requires its current ETag.

Handle 428 and 412 Responses

An update or delete that requires a validator but omits If-Match returns 428 Precondition Required. Read the resource and retry with the ETag from that response.

A stale, weak, or malformed validator returns 412 Precondition Failed. When CEDAR can identify the current revision, the error body's parameters.currentETag reports its tag. Do not simply attach that newer tag to the body you had already prepared: retrieve the current representation, reconcile the other client's change with yours, and then retry using the ETag that accompanied that body.

Representation-specific tags such as "7-yaml", "7-yaml-compact-v2", "7-json", or "7-resource-record" distinguish byte-different renderings of the same stored revision. They are valid If-Match values. Always use the value returned with the representation you actually read. Weak validators such as W/"7" do not satisfy CEDAR's write precondition.

Use the ETag for the Resource Being Changed

Artifact content, permission documents, folders, categories, groups, and group-membership documents are independently revisioned. Read the same REST resource that the later operation will change:

Change Read first Send its ETag with
Template, element, field, or instance content GET the artifact PUT or DELETE that artifact
Artifact, folder, or category permissions GET .../permissions PUT .../permissions
Folder or category properties GET the folder or category its update or delete operation
Group properties GET /groups/{id} on the group service PUT, PATCH, or DELETE /groups/{id}
Group membership GET /groups/{id}/users on the group service PUT /groups/{id}/users

An artifact's content ETag is not a substitute for its permissions ETag, and a group's ETag is not a substitute for its membership ETag. For other mutation commands, use the read operation named by the endpoint's If-Match description in the interactive API documentation.

Use If-Match: * Deliberately

CEDAR accepts the HTTP wildcard:

If-Match: *

The wildcard means “perform this operation if a current resource exists, whatever its revision.” It is useful for an intentional delete-if-present or overwrite-latest operation, but it gives up lost-update protection. If somebody changes the resource after your client reads it, * still matches and your request can replace or delete their work. Interactive editors and ordinary read-modify-write integrations should therefore use the exact ETag from GET.