Why moving Artemis JMS workloads to Kafka is a semantics project before it is a tooling project
If your applications send and receive through JMS queues and topics on ActiveMQ Artemis and you plan to move them to Apache Kafka, the central work is changing how consumers behave, not switching client libraries. JMS acknowledges and removes a message once a consumer takes responsibility for it, while Kafka keeps records in a partitioned log and tracks a position per consumer group. That difference drives ordering, redelivery, fan-out and failure handling, so each queue and topic needs a decision about its new consumption model before anyone chooses a connector or writes a migration script. The Camel JMS Artemis source connector is one documented bridge that can copy traffic from Artemis into Kafka during a staged cutover, but it moves messages without settling these semantics for you.
The effort depends mainly on how many JMS features your applications rely on. Message selectors, durable subscriptions, transacted sessions, message priorities, delayed or scheduled delivery, and dead-letter and expiry handling in Artemis each need an explicit equivalent or an accepted gap in Kafka. A queue used by a few stateless consumers with idempotent handling is a much smaller job than a set of durable topic subscribers that depend on selectors and broker-side redelivery counts. This page does not set duration, staffing or cost figures for that work. Before you request a quote, ask the partner to inventory each destination against the JMS features it uses, confirm the Camel component versions and the Kafka release you will run, and list which Artemis behaviors the business has agreed to change.
Which Artemis JMS concepts map cleanly to Kafka and which do not
This table compares ActiveMQ Artemis JMS concepts with Kafka topics, partitions, consumer groups, offsets and retention. Each row marks whether the concept maps cleanly, maps with redesign, or has no direct equivalent, and names what to confirm in your own environment before you move a workload.
| Artemis concept | Kafka equivalent | Mapping and what to confirm |
|---|---|---|
| Queues (point-to-point) | Topic with a single consumer group | Maps with redesign. Kafka has no native queue. Each partition is consumed by one member of a group, so parallelism is capped by partition count. Confirm expected consumer count and partition plan. |
| Topics (publish-subscribe) | Topic with one consumer group per subscriber | Maps cleanly. Each subscribing application gets its own consumer group and independent offsets. Confirm which applications subscribe today and how many groups they need. |
| Durable subscriptions | Consumer group with committed offsets | Maps with redesign. Offsets persist per group, which resembles a durable subscription. Confirm that subscriber names map to stable group IDs and that offset resets are an approved operational step. |
| Message selectors | Consumer-side filtering or separate topics | Maps with redesign. Kafka has no broker-side selector syntax. Filtering moves into consumer code or stream processing, or traffic is split across topics. Confirm which selectors are in use and their volume. |
| Message ordering | Ordering per partition, set by message key | Maps with redesign. Order holds only within a partition. Global JMS ordering across producers is not kept. Confirm which business keys need ordering and whether partition-level order is enough. |
| Acknowledgement and redelivery | Offset commit by consumer | Has no direct equivalent in its per-message form. Kafka tracks a position, not individual acknowledgements. Failed messages are re-read from the last committed offset. Confirm how applications rely on per-message redelivery counts today. |
| Dead-letter addresses | Dead-letter topic built by the consumer or a connector | Maps with redesign. Kafka does not route poison messages on its own. Teams publish failures to a separate topic in application code or through connector error handling. Confirm retry limits and who owns the dead-letter topic. |
| Transactions (JMS sessions) | Producer transactions and idempotent producers | Maps with redesign. Kafka transactions span writes to multiple partitions and offsets for consume-transform-produce flows. They do not span JMS sessions or external systems. Confirm which JMS transactions include non-Kafka resources. |
| Message TTL and expiry | Time-based or size-based retention per topic | Maps with redesign. Retention applies to the topic log, not to individual messages, so expired messages are not removed per message. Confirm required lifetimes and whether consumers depend on expiry behavior. |
| Message persistence and paging | Log segments on broker disks, governed by retention | Maps cleanly in intent. Storage sizing changes because retained data is kept by time or size, not by consumption. Confirm disk, replication factor and retention targets before cutover. |
Which documented paths move JMS traffic into Kafka
The Camel JMS Artemis source connector reads messages from Artemis queues or topics and writes them to Kafka topics, so existing JMS producers can keep publishing while consumers move over in stages. It is the bridged path for a migration where Artemis stays in service during cutover. The connector is a third-party component that you select, license-check and version-pin yourself, so confirm its license terms, its supported Artemis and Kafka Connect versions, and how it maps JMS message properties to Kafka headers before you rely on it.
- Source connector only: it moves traffic one way, from Artemis to Kafka, so consumers must be switched before Artemis is retired.
- Message mapping: confirm how message bodies, JMS headers and properties, and message IDs map to Kafka keys, values and headers.
- Acknowledgement behavior: check when the connector acknowledges messages on the Artemis side, since that determines duplicate and loss exposure during failover.
- Dependency footprint: you will run the connector plugin jars alongside your Kafka Connect workers.
Before committing, ask the connector maintainer for the tested Artemis and Kafka versions and reproduce a failover test on your own topology.
The bridge runs inside Kafka Connect, which is a separate process layer from the brokers. Connect runs in distributed mode for production, with workers that share configuration and offset state stored in Kafka topics. Plan this runtime as a service of its own: it needs its own capacity, upgrades, monitoring and access control, and its Java version must match the Kafka release you run, since brokers and Connect require Java 17 from Kafka 4.0 onward.
- Distributed workers: run at least two workers so a single failure does not stop the bridge.
- Internal topics: the config, offset and status topics need replication settings planned up front.
- Secrets: Artemis credentials and TLS material belong in a secrets store, not in connector JSON files.
- Java runtime: match the JVM to the Kafka release you deploy, per the upgrade notes.
- Restart behavior: test connector restarts and task rebalances against live JMS traffic.
Confirm the Connect deployment model with your platform team before the bridge design is final, because it sets who is paged when the bridge stalls.
The direct path replaces JMS producers and consumers with Kafka client code. Each application changes its send and receive logic, and you decide the topic layout, partition keys and consumer group names yourself. This route avoids running a bridge, but the work sits in application teams' code and tests. It also means ordering, redelivery and acknowledgement behavior must be rebuilt on Kafka semantics rather than carried over from JMS sessions.
- Topic design: map queues and topics to Kafka topics and choose partition keys that preserve per-entity ordering.
- Consumer groups: each independent consumer application needs its own group ID and committed offsets.
- Retry and dead letters: build retry topics or dead-letter handling explicitly, since JMS redelivery counts do not transfer.
- Transactions and idempotence: decide where exactly-once guarantees are required and enable idempotent producers where they are.
- Testing: run old and new consumers against the same data in a staging environment before cutover.
Ask each application owner to list which JMS features their code depends on, such as selectors, durable subscriptions or message groups, because each one needs a Kafka design decision.
No documented one-step converter moves Artemis journal data into Kafka. The Artemis journal is internal broker storage, and Kafka does not read it. Messages still in Artemis queues at cutover must either be drained by consumers, bridged by the connector until empty, or replayed from an upstream source by the producing application. Treat any tool that claims to convert the journal directly as unverified until you have tested it on your own data.
- In-flight messages: decide whether to drain queues to empty before cutover or to bridge until the backlog clears.
- Durable subscriptions: unconsumed messages for offline subscribers need an explicit handling decision.
- Historical data: Artemis is not a system of record for long history, so confirm whether any replay requirement exists.
- Duplicates: overlapping delivery during cutover can produce duplicates, so consumers should tolerate them.
- Cutover window: the period of parallel operation is set by your backlog and consumer readiness, not by a tool.
Ask the team that operates Artemis for queue depths and subscription inventories so the cutover plan starts from real numbers.
How a bridged cutover from Artemis to Kafka runs in phases
This sequence moves JMS traffic from Artemis to Kafka in gated stages, so each queue or topic is only retired after Kafka has carried its traffic and the team has evidence that nothing was lost or duplicated.
Inventory queues, topics, selectors and consumers
Your team, with DeployOpen if engaged, lists every Artemis address, queue and topic, durable subscription, message selector, consumer application and producer. For each one, record the expected message rate, message size, whether ordering matters, and whether the consumer uses JMS acknowledgment modes or transactions. Selectors and durable subscriptions need the most attention, because they have no direct equivalent and must be mapped to a topic, a key or consumer-side filtering. The step is done when every JMS destination has an owner, a target Kafka topic name and a documented ordering requirement, and the owners have signed off on the list.
Build a pilot topic with one producer and one group
Pick one low-risk queue and create a Kafka topic with a partition count and retention settings chosen from the inventory. Connect one producer and one consumer group to it, and confirm the consumer can read the messages produced to it. Check that the message keys, headers and payload encoding survive the trip, and that the consumer's commit behavior matches what the application expects. The step is done when the pilot runs end to end in a non-production environment, with the message format and any header mapping written down for the rest of the migration.
Configure the bridge and start the parallel run
Deploy the Camel JMS Artemis source connector on Kafka Connect, configured to read from the pilot destination in Artemis and write to the matching Kafka topic. Keep Artemis as the system of record: producers and existing consumers continue to use it. The bridge copies messages into Kafka, so the team can compare both sides. Confirm the connector runs with its tasks healthy and that its acknowledgment behavior is understood, since the bridge decides when a message is removed from Artemis. The step is done when the connector status is stable and the pilot topic receives the same traffic as the Artemis destination.
Reconcile counts and duplicates during the parallel run
While Artemis stays live, compare message counts between the source destination and the Kafka topic over a period the team agrees on, and track the difference as an explicit exception list. Test duplicate handling by checking whether the consumers on the Kafka side are idempotent, and by reviewing redelivery in Artemis that can cause the bridge to publish the same message twice. Watch consumer lag on the new consumer group to confirm it keeps up with the producer rate. The step is done when counts reconcile within the tolerance the owners set, duplicates are either absent or handled by the consumer, and lag stays within the agreed limit.
Cut over consumers in stages
Move consumer applications one group at a time from the Artemis destination to the Kafka topic, starting with the least critical. For each group, stop the Artemis consumer after a clean drain, start the Kafka consumer from a known offset, and confirm it processes new messages. Producers can continue to write to Artemis while the bridge keeps Kafka current, or move later once consumers are stable. Keep the bridge running for rollback until the group has run through its normal workload. The step is done when each consumer group has processed its traffic from Kafka, the offsets and lag are recorded, and the owner of the application has confirmed correct behavior.
Move producers and confirm end-to-end flow
Switch producers from Artemis to Kafka once every consumer for a destination reads from Kafka and the rollback path has been tested. Each producer change is a code or configuration change with its own release and test. Confirm that messages published directly to Kafka arrive with the same keys, headers and ordering guarantees the consumers depend on. Keep the Artemis destination empty and monitored for stray traffic. The step is done when producers write to Kafka, Artemis shows no new traffic on the migrated destinations, and consumers report no gaps or unexpected duplicates over the period the team set.
Decommission Artemis after the final checks pass
Before removing an Artemis destination, confirm that no producer or consumer still uses it, that the bridge has been stopped and its last offsets recorded, and that any messages left in the queue were drained or archived. Keep a backup of the Artemis broker configuration and journal for an agreed retention period so that recovery is possible. Remove the destinations, then the broker, one at a time. The step is done when the owners have signed off on the checklist, the archive is verified, and monitoring shows no activity on the retired Artemis addresses.
What drives the effort and cost of an Artemis to Kafka move
How does the number of queues and topics change the effort of a move?
The count of destinations, and how many of them carry selectors, sets the size of the mapping work more than raw message volume does. Each JMS queue or topic must be mapped to a Kafka topic, a partitioning key, and a consumer group design, and each selector-based subscription must be rewritten as consumer-side filtering or split into separate topics. This mapping is a one-time design effort. Ongoing work recurs whenever new destinations are added, so ask the quote to list every destination, its consumers, and each selector by name.
Do JMS transactions and XA need to be redesigned for Kafka?
Yes, they usually need the most design attention. Local JMS transactions and XA transactions that span Artemis and a database do not carry over directly. Kafka offers idempotent producers and transactions between Kafka topics, but a transaction spanning Kafka and an external database needs an outbox or similar pattern. Any service that relies on two-phase commit will need a changed consistency model, so the quote should name every transacted flow and the systems involved.
How do ordering and priority requirements affect the work?
Ordering guarantees within a Kafka partition are strong, but they do not match Artemis message priority or global ordering across a destination. Priority has no direct Kafka equivalent, so consumers that depend on it need a new scheduling approach. Ordering that must hold per entity can be achieved with a stable message key, which affects partition count and rebalancing behavior. Ask for a list of the flows where ordering or priority is a business rule, since these take the longest to verify.
How much does redelivery and dead-letter handling change the build?
Artemis handles redelivery counts, delayed redelivery and dead-letter addresses on the broker side, while Kafka consumers track their own offsets and error handling. Each consumer needs a retry policy, a dead-letter topic or equivalent, and a way to replay failed records. Teams often underestimate this because it touches every consumer. Ask the quote to cover retry limits, poison message handling, and the tooling used to inspect and replay dead-lettered messages.
Do message size and body format change the cost of the move?
Yes. Large messages, binary payloads, and mixed formats such as JMS object messages increase conversion and testing effort. Kafka brokers and clients have their own size limits that must be set deliberately, and Java object serialization in JMS messages typically needs replacing with a documented format. Ask for an inventory of body types, average and maximum sizes, and any use of JMS object messages, along with the schema approach agreed for the new payloads.
How does the number of client applications and languages affect the quote?
Each client application is a separate unit of work: it needs a new producer or consumer, tests, and a cutover step. Clients written in several languages need several client libraries and their own conventions for offsets and error handling. Ask for a list of applications with their language, JMS API version, owner, and whether the code can be changed. Applications that cannot be changed may need a bridge, which adds recurring operation.
What must be rebuilt from the Artemis security model in Kafka?
JAAS login modules, TLS listeners, and broker roles do not transfer as-is. In Kafka, clients authenticate with SSL or SASL, and the documented SASL mechanisms include GSSAPI, PLAIN, SCRAM-SHA-256, SCRAM-SHA-512, and OAUTHBEARER. Authorization in KRaft clusters is handled by the StandardAuthorizer, which stores ACLs in the cluster metadata log, and resources without ACLs are restricted to super users unless allow.everyone.if.no.acl.found is set to true. Each Artemis role must be mapped to explicit Kafka ACLs, and that mapping is a one-time task that must be tested per application. Confirm the chosen identity source with your security team.
Which costs are one-time and which recur after cutover?
One-time costs include discovery, destination mapping, transaction and ordering redesign, client changes, the bridge build, security mapping, and cutover testing. Recurring costs include running the Kafka cluster, the Camel JMS Artemis source connector while the bridge is in use, monitoring, certificate and credential rotation, and upgrades. Count each environment separately: development, test, staging, and production each repeat the verification work. In the quote, ask for the one-time and recurring totals to be shown as separate lines.
How ordering and redelivery change when JMS consumers read from Kafka
Kafka orders records within a single partition, not across a topic, so the ordering guarantee your Artemis queue gave you has to be rebuilt from partition assignment. A JMS queue delivers messages to whichever consumer is free, and that consumer competes with its peers for the next message. In Kafka, a consumer group assigns each partition to exactly one consumer at a time, so all records for a partition pass through one consumer in offset order. Map each ordering requirement to a key: records that must stay in order need the same partition key, such as an order or account identifier. Confirm with your application owners which queues depend on strict order and which only needed throughput, because a single-partition topic removes parallelism and a many-partition topic removes global order.
JMS acknowledgement and Kafka offset commits solve the same problem differently. With Artemis, a message is acknowledged or redelivered per message, and the broker tracks that state for each consumer. Kafka does not track acknowledgement per record. A consumer commits an offset, which marks everything before it in that partition as processed, so one slow or failed record holds back the commits behind it. Decide whether your consumers commit after processing (at-least-once) or before it, and test what happens when a consumer crashes between processing and committing. Redelivery in Kafka is a replay from the last committed offset, which can resend records that were already handled. Poison messages, which fail on every attempt, block their partition until your code skips them, retries them with a limit, or routes them to a dead-letter topic. Plan that routing explicitly, because Kafka has no built-in dead-letter behavior for JMS-style redelivery counts.
Because replay and rebalancing can deliver a record more than once, consumers must be idempotent. Store a business key or event identifier with each side effect and check it before acting, or write results through an upsert keyed on that identifier. Rebalances, which occur when consumers join or leave a group, also pause consumption for the affected partitions, and the exact pause and commit behavior depends on your client version and configuration. Test these paths against your own workload: force consumer crashes mid-batch, introduce a poison record, scale the group up and down, and compare the duplicate and out-of-order counts with what your Artemis setup produced. The Kafka consumer documentation and your client library release notes are the references to confirm defaults for the versions you run.
What usually goes wrong in an Artemis to Kafka migration
Most problems in this migration come from assumptions that held in JMS and do not hold in Kafka. Duplicates, lost routing logic, ordering, security and redelivery all change shape once messages live in a partitioned log. Each card below names one risk and the check to run before cutover.
Duplicate delivery during the parallel run
While the Camel bridge copies messages from Artemis into Kafka and consumers still read from both sides, the same business event can be processed twice. Duplicates are expected when a bridge retries after a timeout, so consumers must be idempotent before any application is pointed at Kafka. Check that each consumer records a business key and skips keys it has already handled, and test this with a deliberate replay of a batch.
Lost selector logic
JMS consumers often filter messages with selectors on message headers or properties, such as JMSType or custom string fields. Kafka has no selector equivalent, so that filtering moves into consumer code, a stream processor or a separate topic per route. Inventory every selector in use, document which consumer applied it, and confirm that each filter has a new home before the old subscription is retired.
Ordering assumptions broken by partitioning
Artemis delivers a queue in order to its consumers, while Kafka only guarantees order within a single partition. If messages for one account or order are spread across partitions by a round-robin or unrelated key, consumers will see them out of sequence. Choose a message key that matches the entity whose order matters, and verify with a test that messages sharing that key land on one partition.
Security rules not carried over to Kafka ACLs
Artemis users, roles and address-level permissions do not transfer automatically. In KRaft clusters, the StandardAuthorizer stores ACLs in the metadata log, and resources without ACLs are limited to super users unless allow.everyone.if.no.acl.found is set to true. Map each Artemis role to explicit Kafka principals and topic ACLs, then test access for every producer and consumer group, including the bridge account.
Bridge throughput limits
A Camel JMS source reads from Artemis through JMS sessions and acknowledgment modes, so its throughput depends on prefetch, batch size and the Kafka producer settings behind it. A bridge that keeps pace in tests can fall behind during peak hours, and the backlog grows on the Artemis side while the cutover window closes. Load test the bridge with production message sizes and peak rates, and set alerts on the queue depth in Artemis.
Consumers that relied on JMS redelivery
JMS sessions redeliver a message when it is rolled back or not acknowledged, and many consumers depend on that behavior without documenting it. Kafka consumers do not redeliver on their own: they track an offset, so a failed message stays in the log unless the consumer re-reads it or sends it to a retry or dead-letter topic. Decide the retry and dead-letter policy for each consumer, and test a poison message to confirm it does not block its partition.
What teams ask before moving Artemis workloads to Kafka
What does a migration from ActiveMQ Artemis to Kafka cost?
The cost depends on the scope agreed in writing, and no figure is given here. The main components are the discovery of queues, topics and consumers, the design of topics and partitions, the build of producers and consumers, the bridge or connector work, testing, cutover and handover. Infrastructure is a separate line: Kafka itself is licensed under Apache-2.0, so the recurring spend is compute, storage, network and the people who run the cluster. Costs rise with the number of JMS destinations, the number of consumer applications that need code changes, the amount of ordering and redelivery logic that must be reproduced, and the number of environments. Ask any provider for a quote that lists one-time work separately from recurring support, upgrades and testing.
How long does an Artemis to Kafka move take?
The timeline is set by the number of destinations and applications, not by the broker itself. Teams usually need time to inventory JMS queues, topics, selectors, durable subscriptions and message formats, then decide which consumers can move first. Other drivers are how many consumers rely on JMS-specific behavior such as per-message acknowledgment and redelivery, whether the bridged period needs both systems running, the change windows available in your production environment, and the approvals required for new infrastructure. Agree on milestones and the exit criteria for each cutover phase before work starts, and confirm durations with your own environment rather than with generic estimates.
Can our own team do this migration without outside help?
Yes, many teams do, provided they have engineers who know both the JMS API and Kafka's consumer model. The gaps usually appear in three places: translating queue semantics into partitions and consumer groups, reproducing acknowledgment, retry and dead-letter behavior in application code, and operating a KRaft cluster with TLS, authorization and monitoring. Ask whether the team has done a bridged cutover before and who will own the cluster after go-live. A partner can take on a fixed scope, such as the topic design, the connector setup and the cutover runbook, while your team keeps ownership of the business logic.
What do our developers and operators need to provide?
Developers need to name the JMS consumers and producers, describe their acknowledgment and retry logic, and say where message order matters. They also need to confirm message bodies, headers and property names, because these become the Kafka record format and the contract consumers depend on. Operators need to provide the network paths between Artemis and Kafka, identity and certificate sources, the monitoring and alerting stack, backup expectations, and the change calendar. Both groups should agree who signs off on each cutover step, and who can roll a consumer back if a redelivery problem appears in production.
Can Artemis keep running during the migration?
Yes, a staged move is normally planned so that Artemis keeps serving existing consumers while Kafka takes over one flow at a time. A bridge can copy messages from JMS destinations into Kafka topics using the Camel JMS Artemis source connector, so that new consumers read from Kafka while legacy consumers finish draining Artemis. The period in which both systems run is a cost and risk factor: it adds infrastructure, duplicate-processing checks and reconciliation work. Confirm with the Camel and Kafka Connect documentation how the connector acknowledges messages, and test failure and restart behavior in a non-production environment before the first production flow moves.
Why does ordering and redelivery change after the move?
Kafka guarantees order only within a partition, while a JMS queue delivers messages to competing consumers in a way that does not map one to one onto partitions. If a consumer depends on strict order for a business key, the topic needs a partitioning key that keeps that key in one partition, and the consumer group must be sized so that partitions are not reassigned mid-stream without care. Redelivery also differs: Artemis tracks acknowledgment per message, while Kafka consumers track offsets per partition, so a failed message usually needs retry topics or a dead-letter pattern written by the application team. Test these paths with real failure injection before cutover.
What should we ask a provider before we sign a scope?
Ask for a written inventory of the destinations in scope, a list of consumers and producers with their acknowledgment and ordering requirements, and a topic and partition design that each consumer owner has reviewed. Ask how the bridged period will be tested, how rollback works for each flow, what evidence is delivered at cutover, and who operates the Kafka cluster afterward. Ask which upgrades and security testing are included, and whether support or retainers are priced separately. Confirm all of this in writing, since a fixed-scope agreement is the document that defines what is delivered.
How DeployOpen scopes and delivers an Artemis to Kafka migration
An Artemis to Kafka move touches the cluster, the bridge, the consumers and the operating model at once, so the work is scoped as one written plan. The cards below follow that plan from the agreed scope through to handover and optional ongoing support.
Written scope before work starts
Most engagements are fixed-scope, so the queues and topics in scope, the Artemis versions and clients involved, the cutover approach and the deliverables are agreed in writing before any build begins. Ask for the inventory of JMS destinations, selectors, durable subscriptions and message volumes to be listed as scope items. Anything not listed is a change request, which is where cost surprises usually come from.
Kafka cluster planning in your infrastructure
The Kafka cluster is designed and built inside your own cloud accounts, data centers or restricted networks, using KRaft mode with planned partition counts, retention and access control. Topic design follows the destination inventory, so each JMS queue or topic maps to a deliberate topic and key strategy. Confirm early which Kafka release line your platform team will support, since upgrade paths depend on it.
Bridge configuration and security mapping
The Camel JMS Artemis source connector is configured to drain Artemis into Kafka during a bridged cutover, with credentials, TLS and Kafka ACLs set up to match your current Artemis security model. Access rules are recreated as ACLs in the Kafka metadata log rather than copied one to one. Verify each queue's producer and consumer identities before the bridge goes live.
Consumer rework and ordering review
Consumers move from JMS sessions and acknowledgement modes to Kafka consumer groups, and each one is reviewed for ordering assumptions, redelivery handling and duplicate tolerance. Idempotent processing is usually the main change, so the team should expect code work in every consumer, not only in the bridge. Identify which consumers depend on strict per-queue ordering before scoping their rewrite.
Testing, cutover and documentation
Testing covers message delivery counts, ordering under load, redelivery after consumer failure and rollback during cutover, run against your own staging environment. Deliverables include runbooks, configuration records and the cutover checklist, so your team can operate the result. Ask for test cases and acceptance criteria to be written into the scope document.
Optional retainers for ongoing support
After handover, ongoing support, upgrades and continuous testing can be taken on through a retainer rather than an ad hoc engagement. A retainer suits teams that lack in-house Kafka operations capacity during the first year of running the cluster. Ask for the retainer's response expectations and upgrade responsibilities in writing, and confirm what is excluded.

