Skip to content

v4.0.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 29 Oct 14:44
3adf1a3

connect-library 4.0.0, attachment-receiver 4.0.0, connect-e2ee 4.0.0 (new)

Major version. Adds optional end-to-end encryption of Connect cases and notes, to the tsanet-e2ee-1 profile in the workgroup specification "Connect E2E Encryption" (tsanetgit/Connect_SDK#140). The platform holds no keys: each member encrypts and signs at its own connector. A connector that doesn't use E2EE needs no change to its calls. Runtime unchanged: Spring Boot 4.1, Jackson 3, JDK 21.

This release also carries everything in 3.0.0 (published 2026-10-21), repeated under "Included from 3.0.0" so a member coming from 2.0.0 has one list. Coming from 2.0.0, apply the 3.0.0 changes first.

The full upgrade guide is the 3.0.0 and 4.0.0 entries in skills/connect-sdk-deploy/references/consume-artifact.md.

New in 4.0.0

  1. connect-e2ee, a new optional artifact (#143). It encrypts and signs a case or note for a create, and verifies and decrypts one on a read.
    • Sending: E2eeProfile.encryptCase and encryptNote check the plaintext (required form fields and the spec's lengths), encrypt each protected field for the partner and the sender, and sign a manifest. The result goes straight into the create overloads below.
    • Receiving: verifyCase and verifyNote run the profile's six checks in order. The result is the decrypted content, or a refusal naming the step and the field that never carries a value. There is no plaintext fallback.
    • Keys: private keys stay behind KeyOperations, so they can live in a key service (SoftwareKeyOperations is for tests). Partner public keys come from pinned JWKS URLs (PinnedJwksPartnerKeys).
    • Replay: a ReplayGuard checks first receipt (an in-memory one is included).
    • Dependency: the module brings com.nimbusds:nimbus-jose-jwt 10.10. A connector that doesn't add the module gets no JOSE library.
    • Setup and the full API are in connect-e2ee/README.md.
  2. connect-library carries the E2EE fields (#141, #142).
    • createRequest (both forms) and createNote gain an overload taking E2eeFields(manifest, encrypted), sent exactly as given. With encrypted true, values are sent unstripped and the note length limits don't apply.
    • CaseNoteDto gains manifest and encrypted.
    • CollaborationRequestStatusDto gains manifest, encrypted, submitterCaseNumber, description and customFields (CaseCustomFieldDto(id, fieldName, value)).
    • The SQLite cache stores all of them, adding the columns to an existing cache on first open.

Breaking changes in 4.0.0

Existing calls compile, and a consumer that bumps without recompiling still links. Two kinds of code break at source, which is why this is a major:

  • Implementers and decorators of CollaborationRequestsFacade or CaseNotesFacade must add the new abstract overloads, and move their logic to them. The old overloads now forward to the new ones, so an override of the old overload alone no longer sees an E2EE create. Point interceptors and test stubs at the new overloads too. E2eeFields is in connect-library, so this needs no connect-e2ee.
  • Record patterns over CaseNoteDto or CollaborationRequestStatusDto gain components, so a 3.0.0 pattern stops compiling. The earlier constructors are deprecated but still work, which a -Werror build will flag.

Also check in 4.0.0

  • Records cached by 3.0.0 read the new components as null ("not known") until fetched again. On stored reads, null-check customFields and test Boolean.TRUE.equals(encrypted()) rather than unboxing encrypted(), which throws on an old row. For the same reason an old record doesn't equal a fresh read of the same case or note.
  • Logs and JSON: toString and JSON serialization include the new components, so logging or forwarding a CollaborationRequestStatusDto now carries the case's description and custom-field answers, not only its summary.
  • Case text on disk: the cache now holds each case's description and custom-field answers as well as its summary. For an encrypted case it holds the ciphertext the platform returned; connect-e2ee never writes decrypted text to it. CacheRetention.sweep evicts closed and rejected cases; it never sweeps an open one.

Known limits in 4.0.0

  • Form rules: connect-e2ee checks required and lengths, but not a form field's options or validationRules, whose format the API doesn't document (tsanetgit/Connect-API-Code#221).
  • Two encryption keys: a partner publishing two encryption keys is refused until the workgroup sets a rule. Publish only your current encryption key.
  • Sanitizer workaround: while the API's sanitizer rewrites encrypted values (tsanetgit/Connect-API-Code#214), the module restores them before checking, as the Power App does.

Included from 3.0.0

The V2 attachment client is rebuilt on the grant model in the Connect OpenAPI spec, and its public types change incompatibly. Senders can restrict which companies receive their V2 attachments, and receivers can configure their own storage. Case notes take their types from the server. /v1/ failures now arrive as problem details, and Connect API calls run on the JDK's HTTP client.

Breaking changes in 3.0.0

  1. The V2 attachment client is rebuilt from the spec (#96, #100). attachmentsV2() has one method per Connect API call: createGrant, getGrant, listGrants, singleUploadLink, s3PartLinks, azureBlockLinks, the three complete* calls and abandon. It also keeps complete for the grant's mode, upload and a four-argument send that returns the completed AttachmentGrant.

    • Removed: grant, the old upload and complete signatures, the six-argument send, and AttachmentGrantRequest, AttachmentCompleteRequest and AttachmentCompleteResult.
    • AttachmentGrant is now the server's grant: a long id, Status, UploadMode and UploadPlan. It takes no content type, SHA-256 or description, and has no DELIVERED or note-id outcome.
    • AttachmentV2Exception: problemType() and isProblem are replaced by code() and is(code), with codes chosen by HTTP status. Several 2.0.0 codes are removed; the upgrade guide maps each one.
    • send no longer re-grants when a grant expires before complete.
  2. /v1/ failures are problem details (#127, #129). The library now sends Accept: application/json, application/problem+json. A failed /v1/ call now carries the documented 4xx status where 2.0.0 often saw a 500, with type() and title() set, and the message reads HTTP <status> <title> (<type suffix>): <detail>. Code that branches on kind() == LEGACY, on status() or on the message text sees different values: sweep for it. A wrong password is a PROBLEM too.

  3. Connect API calls use java.net.http.HttpClient (#128, #131 to #134), one client per session. The JVM's proxy selector and SSL context still apply.

    • No longer honored: a default Authenticator for an authenticating proxy, and HttpsURLConnection's default socket factory and hostname verifier.
    • Redirects: a redirected GET is no longer followed. Any 3xx is OTHER, and the message never names the Location.
    • Interrupts: an interrupted call stops with OTHER, status 0, and leaves the thread interrupted.
  4. Interface and record changes.

    • TsaNetApiSession gains attachmentStorage().
    • CaseNotesFacade gains listNoteTypes() and a five-argument createNote.
    • ApplicationUserAccount and TsaNetApiConfiguration gain an allowedReceiverCompanyIds component.
    • CaseNoteDto grows from 14 to 18 components.

    Callers are unaffected. Implementers and decorators must add the new methods. A record pattern over a changed record stops compiling. The 2.0.0 constructors still compile, and the CaseNoteDto one is deprecated.

  5. A V2 upload link missing expiresAt or sizeBytes is refused (#138). An upload-link answer without expiresAt (any link), or without sizeBytes (an S3 part or Azure block), fails with AttachmentV2Exception CLIENT_PRECONDITION "the answer has no ...". It used to silently turn off link refresh or the part-size check. The spec requires both fields, and the platform sends them.

New in 3.0.0

  • Receiver allowlist, part 1 of #92 (#104). Set it with ApplicationUserAccount.withAllowedReceiverCompanyIds, or with allowed-receiver-company-ids in the console's account config.
    • With a list set: createGrant (and so send) refuses a case whose receiving company isn't on it with attachment/receiver-not-allowed, before any grant is requested. The platform's own allowlist refusal (403) is reported as the same code.
    • Empty or null: unrestricted.
  • Receiver storage (#106, #107). session.attachmentStorage() gets, registers and tests the company's S3 or Azure Blob storage. The demo-ui has a Settings page for it, read-only unless TSANET_DEMO_RECEIVER_STORAGE_EDITABLE is set.
  • Note types from the server (#99, #103, reworked in #124 to #126).
    • Listing types: listNoteTypes() returns the codes a caller may send, from GET /v1/note-types.
    • Creating: createNote(..., type) sends the code as given, and a code the server doesn't offer is a 400 invalid-note-type.
    • No default: a null type creates a note with no type, and nothing becomes partner.
    • Reading notes: they carry type, typeDisplayName, companyId and direction.
    • The SQLite cache adds the four columns at startup.
  • Upload loop on the spec's links.
    • Links: requested just before use, in batches of up to 1,000, and refreshed when they expire within 60 seconds.
    • Retries: bounded per part, honoring Retry-After.
    • Wire: link headers are sent unchanged, S3 ETags are kept for complete, and links never appear in logs or messages.

Unchanged

  • V1 attachments and every other existing call, apart from the additions above.
  • Runtime line: Spring Boot 4.1, Jackson 3, JDK 21.
  • attachment-receiver: its source is unchanged since 2.0.0. It is published at 4.0.0 because the modules release together.

Consuming the artifacts

Get com.tsanet:connect-library:4.0.0, com.tsanet:attachment-receiver:4.0.0 and, for E2EE, com.tsanet:connect-e2ee:4.0.0 from https://maven.pkg.github.com/tsanetgit/Connect_SDK. A read:packages token is required even for public packages.

Building from source needs a Connect-API-Code spec branch that has the Attachment Grants and Attachment Storage Config operations and the E2EE fields. beta has them.

The V2 attachment endpoints are x-stability-level: alpha in the spec.

Known consumers

  • tsanetgit/Connect_Gateway pins connect-library 3.0.0 (gateway-engine/pom.xml). Taking E2EE is tsanetgit/Connect_Gateway#51: bump to 4.0.0, add connect-e2ee, and wire it into create and fetch. The 4.0.0 facade change applies to any decorator it has.
  • tsanetgit/Fin-Intercom_App consumes gateway-engine, so it follows the gateway.

Verification

  • CI at the release commit: verify on main 3adf1a3 (the merge of #145), built against Connect-API-Code beta 46fc91f. Every module passed with zero failures:

    Module Tests
    connect-library 317
    connect-e2ee 57
    scripted demo 9
    console 44
    attachment-receiver 182 (40 environment-gated live tests skipped)
    demo-ui 44
  • Cross-implementation: connect-e2ee decrypts and verifies the specification's worked example (Addendum D), which was generated independently with other JOSE libraries, for both members. The tests include every refusal step and 35 red probes, each removing one check and showing a test fail.

  • Reviews: #141, #142 and #143 each had a design-critic run, the pre-PR gate, auto-QA and a separate-session QA review, with every finding dispositioned on its PR. The upgrade guide (#144) had two separate-session QA rounds. 3.0.0 had a whole-branch design review of its promotion (#136).

  • Live on BETA for 3.0.0 (2026-10-21): the demo-ui image built from the 3.0.0 release commit 23b7489 sent two V2 attachments as a test member, to a test receiver whose storage is an S3 bucket: a 1 MiB file in single mode and a 12 MiB file in s3Multipart mode (three 5 MiB parts), both grants completed.

  • Not yet done for 4.0.0: a live round trip with the Power App on DEV or Beta (tsanetgit/Connect_SDK#140).