Skip to content

Message stream appends are accepted on read-only Waterline v1 projections #769

Closed

Description

@rmcdaniel

Published Server 2.11.1 accepts a message stream append to a read-only Waterline v1 projection.

Behavior. POST /api/workflows/{workflowId}/message-streams/{streamName}/messages (MessageStreamController::append, MessageStreamService::append) never checks whether the instance's current run is a Waterline v1 projection (import_source = waterline_v1_projection). For a projection in an open status (pending, running or waiting), a valid append:

  • creates the inbound stream and item;
  • delivers a signal with its command and history event;
  • creates a workflow task for a run whose execution is owned by the v1 runtime.

An invalid body still records malformed-input state on the projection's stream.

Why it matters. Every other run operation refuses a projection with v1_projection_read_only before reading its body (WorkflowController::rejectProjectedV1Operation / projectedV1OperationResponse: signal, query, update, cancel, terminate, repair, archive, redrive). The append path breaks the projection's read-only, no-tasks contract, and gives workers an execution path into a v1-owned run.

Expected. The append is refused with 409 v1_projection_read_only before the body is parsed. The service also checks under its lock, so a direct call cannot write any stream, item, signal, command, history, task or summary state. Malformed input is not counted against a projection.

Native. Server #768 (Rust #325) adds that refusal at the route and the service, plus a locked check in the store transaction. Its tests show valid and invalid appends, a direct service call and a direct store call change no rows. That will differ from published 2.11.1 until PHP is fixed.

Found by Security's review of #768. Handed to the General owner for the PHP fix.

Current implementation

Cloud's PHP implementation merged in PR #812 on October 28 at 03:55:17 UTC as 5b8bc4d7e856354f3b8fa152c91284e62fd82e40. All 43 Shared jobs, PHP feature qualification and public-boundary qualification passed on the actual cumulative commit.

Focused source qualification passed 43 tests / 992 assertions on both SQLite and MySQL 8.0.46. The previous implementation and former full-event byte meter fail the new controls. The published CLI 2.3.1 HTTP qualification passed 128 checks across all seven projection statuses, including dry run, idempotence, read-only/no-write refusal and independently calculated persisted canonical-byte totals. These remain source-qualification results.

Qualified publication and actual installed PHP acceptance are complete: 243-check published Server2.11.6/CLI2.3.1 receipt, actual October29 14:21UTC, amd64/SQLite. All original recorded consumer cases passed from fresh state after the observer-only cursor correction; all exact task runtime/data/helper resources are removed. Native/shared canonical-meter alignment remains open in#770 and SQLite contention in#796. Newly confirmed bridge regression#861 holds broader release qualification#857 and does not alter this scoped read-only result. Original failed combined/source evidence remains preserved.

Activity

  1. added a commit that references this issue on Oct 25, 2026
  2. rmcdaniel commented on Oct 25, 2026

    @rmcdaniel
    MemberAuthor

    Handoff record. The General owner accepted the PHP fix in the native agent chat (server-325, 2026-10-25 22:58 UTC). The planned shape:

    • A request-time refusal before the body is parsed (the HTTP fence).
    • A check inside MessageStreamService's locked transaction, before both the duplicate-replay update and the new item, so a direct service call cannot write either.
    • No malformed-input counting against a projection.

    Native counterpart. Server #768 does the same in three places: the route, the service, and the store transaction under its serialization lock. Its tests cover valid, invalid and non-object appends, direct service and store calls, and a lock-barrier control showing that an append cannot cross a projection install.

    Merge ordering. Native #768 can merge first and is documented as ahead of published 2.11.1 here. This issue stays open until the PHP Server has the fix and a parity fixture covers it.

  3. rmcdaniel commented on Oct 27, 2026

    @rmcdaniel
    MemberAuthor

    Acknowledged ownership handoff, 2026-10-27: Cloud offered this accepted public PHP slice and General accepted. Cloud now owns the PHP implementation through normal CI/merge, publication coordination and installed acceptance. General has no source branch/worktree/PR in progress for this issue and will preserve Cloud's work.

    The existing acceptance and shared contract above remain in force. Native/frozen fixture edits remain with the Rust Server owner; coordinate exact contract evidence without weakening assertions. Normal CI-first and release controls apply. General continues its separate #794/#796/#777 work; this handoff does not transfer #526's retained polling source work.

  4. rmcdaniel commented on Oct 27, 2026

    @rmcdaniel
    MemberAuthor

    Maintainer handoff accepted: Cloud owns the PHP implementation, focused regression coverage, normal CI/merge, coordinated protected publication and installed acceptance. This includes the pre-body read-only refusal, locked service fence before duplicate/new writes, and zero malformed-input accounting on projections. Native/frozen fixtures remain with their existing owner.

  5. rmcdaniel commented on Oct 29, 2026

    @rmcdaniel
    MemberAuthor

    Cloud installed acceptance passed on published Server2.11.6, linux/amd64 and SQLite, using official CLI2.3.1: 243 checks passed, zero failed,14.249246s. Release40885194236 and matching chart0.3.6 passed their normal publication gates.

    Exact identities: Server source d13ebdb5eccc3d33b9d3c0e1ebc9ab0cb116e121; index sha256:28e1ddeea655760e99748c48f2fa9bbecda37d3924864f6ee9011764144f2c2b; tested GHCR amd64 child sha256:c20c901eaa8dedfa04e5b89280f93b23a5e84e143410e3ceb63ff0881d701641. CLI SHA256 92b7108a2f54573e9ef14c10e59fc58e3268e8b5c62cc8d998a6ec9dab25e290. Core2.15.7 provenance and critical runtime blobs match source3aa98b84; Mime7.4.22 identity matches its release lock.

    The real CLI/HTTP run repeated the recorded128-check consumer scope with additional identity and observation controls:

    • Dry run, import, idempotent repeat and describe passed for pending/running/waiting/completed/failed/cancelled/terminated projections.
    • Eight valid/invalid structured and non-object append shapes per status returned409 v1_projection_read_only, with accepted=false/duplicate=false. Global malformed JSON stayed400.
    • All nine workflow tables retained identical counts and complete-row hashes around every refusal, repeat, describe and dry run; projections created zero tasks.
    • Independent event-type plus canonical-payload byte totals matched every stored summary, including Unicode and line separators. Open fixtures had2events/2578bytes; terminal fixtures had3events with their independently verified totals.
    • Event3/byte16384 overflow controls refused both dry-run and write modes with the correct dimension/used/limit and zero target writes.

    The first attempt exposed an observer defect: an unclosed PDO cursor pinned SQLite's read view across later imports. A held/fresh/closed-cursor control showed2/3/3runs. Only two cursor closes changed; the full unchanged strict assertions then passed from fresh state. No product overlay or assertion removal. Accepted report SHA256 a64d2bcef0cffde8e071378c7d8edbc4555b89c757b73596042446efcfa6f190.

    The isolated UID1000 containers and internal network are gone; fixture state, CLI binary and42task files totaling26,544,979bytes were removed after mount/open-handle checks. Shared images and other owners' resources were preserved.

    This completes the published PHP read-only fix. Native canonical-meter/frozen-reference alignment remains in#770, SQLite contention remains in#796, and the newly found bridge regression#861 holds broader release qualification#857. SDK-Python223/Workflow956 remain open. The installed result above covers amd64/SQLite; the earlier43tests/992assertions perSQLite/MySQL source qualification remains separately attributed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions