Why Hyperledger Fabric monitoring starts with the operations service
The operations service on each Fabric peer and orderer is the built-in source of health, version, log level and metric data, so monitoring starts there. Each node exposes a /healthz endpoint for health checks, a /version endpoint for the running build, a /logspec endpoint for reading and changing log levels at runtime, and a Prometheus /metrics endpoint or a StatsD push. Monitoring is the work of securing those endpoints, collecting the data and deciding which signals raise an alert. The team running the network needs that early warning before an orderer, a peer or a gossip link degrades far enough to stall a channel.
This is harder than it looks because ownership is split. Each consortium member typically runs its own peers and, in many networks, its own orderers, so the data you need spans infrastructure that no single team controls. A central monitoring team may only see the nodes it operates. Before go-live, agree who owns each endpoint, who receives each alert, and who may change a log level on a node that belongs to another member. Confirm with the consortium whether metrics can cross organizational boundaries and over which network paths they will travel.
Every operations endpoint is secured with mutual TLS client certificates, so each scraper, health probe and alerting agent needs a client identity that the node trusts. Monitoring therefore adds to the certificate lifecycle rather than standing apart from it. A Prometheus server whose client certificate expires does not raise an error in the usual sense. It simply stops returning data, and the silence can look like a healthy network. Plan issuance, renewal and revocation for monitoring identities alongside peer and orderer certificates, and confirm with your Fabric CA setup how those identities will be enrolled and renewed.
A passing /healthz check tells you that a process is running and its own checks succeed. It does not confirm that a channel has an ordering leader, that a majority of Raft nodes can reach each other, that clients can reach enough endorsing peers, or that blocks are arriving at every peer through gossip. An orderer can be healthy while its channel cannot order transactions. Build alerts around channel outcomes: block height across peers, orderer cluster leadership and quorum, endorsement failures and gossip lag. Ask your team to verify which of these signals each Fabric version you run actually exposes, since metric names and availability depend on the release.
Which operations endpoints each Fabric component exposes
Each Fabric peer and orderer exposes a small set of operations endpoints that monitoring depends on. All of them require mutual TLS, so every scraper or agent needs a client certificate the node trusts, and the Fabric CA and each organization's nodes add their own endpoints to the monitoring inventory.
Health checks and version reporting
The operations service exposes /healthz on each peer and orderer, which reports node health and is a natural target for load balancer or container liveness probes. The /version endpoint reports the running Fabric version, so check it after every binary upgrade to confirm each node runs the release you planned. Record versions per node in your inventory, because a rolling upgrade leaves nodes on mixed versions for a period. Confirm the response format for your release in the operations service documentation.
Runtime log levels through logspec
The /logspec endpoint reads and changes a node's log level without a restart, so operators can raise verbosity while investigating an endorsement failure or gossip problem. Confirm whether a level change persists across a restart, since a node that comes back with debug output enabled fills disks and obscures signal. Agree on who may change levels in production, and record every change in the incident log. Check the accepted level names against the release you run.
Prometheus metrics or StatsD push
Peers and orderers can expose a Prometheus /metrics endpoint or push metrics to a StatsD server, depending on how the operations section of each node's configuration is set. Pick one pattern per node type and apply it across the network so dashboards and alert rules use the same metric names. Confirm the metric names your alerts depend on against your release, because names can change between versions.
Mutual TLS for every operations call
Every call to the operations service must present a client certificate that the node trusts, so the Prometheus scraper, StatsD agent or probe needs its own identity issued by a CA the node accepts. Settle this before alerting goes live, because a scraper that fails authentication often shows up as a silent gap in data. Decide which CA issues monitoring client certificates, how they are renewed, and who owns the trust configuration on each node.
Fabric CA needs its own monitoring
Fabric CA is a separate service from peers and orderers, so its availability is usually not visible in peer or orderer dashboards. Enrollment, renewal and revocation all depend on it, so an unreachable CA can block certificate operations long before a channel shows a fault. Ask the Fabric CA documentation which operational endpoints it exposes and how they are secured, then add the CA to the same monitoring inventory as the rest of the network.
Endpoints belong to each organization
Each consortium member typically operates its own peers and orderers, so each organization also controls the operations endpoints on its nodes. Monitoring is usually set up per organization, with each member's scrapers reaching its own nodes using its own client certificates. Agree in governance which health or version signals each member publishes to the others and which remain internal, then write scrape ownership into the operating model.
Which metrics pipeline to choose and what each choice costs you
This table compares Prometheus scraping of the operations service /metrics endpoint with StatsD push, and shows what each choice asks of each member organization. Read each row as a decision point, since the in-cluster versus central split changes who holds certificates, retention and pages, and who can see which node's metrics. The main takeaway is that the transport matters less than agreeing ownership of certificates, network paths and alerts before go-live.
| Decision point | Prometheus scraping /metrics | StatsD push |
|---|---|---|
| Best fit | Member already runs Prometheus and can reach each node's operations port over an allowed network path. | Member already ingests StatsD and wants nodes to push outward instead of accepting inbound scrapes. |
| Certificate handling | The scraper needs a client certificate that each node's operations service trusts. Renewal recurs on every scraped node. | Confirm in your release how mutual TLS applies to the push path. Each node and collector still needs a named certificate owner. |
| Network paths across organizations | Each member opens inbound access from the scraper to every node's operations port, one path per organization boundary. | Each node opens outbound access to the collector. Collector address and ports should be agreed in consortium governance. |
| Retention ownership | The party running Prometheus sets retention. In-cluster, each member keeps history for its own nodes only. | The receiving backend sets retention. Whoever runs the collector decides how long every member's data is kept. |
| Who gets paged | In-cluster, each member's on-call receives alerts for its own nodes. A central scraper pages whoever operates it. | The same split applies. The collector operator receives alerts unless each member runs its own receiver and rules. |
| Node visibility | Each member sees only what its own scraper collects. A central scraper gives one operator visibility across all members' nodes. | A central collector gives its operator the same cross-member view. Agree in governance who may read which nodes' metrics. |
| Failure you will miss first | An expired scraper certificate or blocked port makes targets disappear. Alert on scrape failures, not only on metric values. | Pushed data stops silently when a node or path fails. Alert on absence of expected series, not only on thresholds. |
| Confirm before you commit | Confirm metric names and operations service settings for your release line, 2.5 LTS or 3.x, before writing alert rules. | Confirm the StatsD prefix, address and reporting settings for your release line before writing alert rules. |
How to set up Fabric monitoring step by step
This sequence turns the operations service on every peer and orderer into a monitored, access-controlled endpoint that a monitoring host can query. Each step ends with a check you can run, so gaps in certificates, TLS or the metrics pipeline surface before alerts depend on them.
Enable and bind the operations service
Platform engineers set the operations listen address on every peer and orderer and enable TLS on that listener in each node's configuration. Restrict the address to the monitoring network and keep it off public interfaces. Restart each node, then confirm the health endpoint answers from the monitoring host's network segment. The step closes when /healthz returns a healthy status from every peer and orderer in the network, including the nodes you configured last.
Issue monitoring client certificates from the node CA
The PKI or security team enrolls a client identity for the monitoring agent through the Fabric CA that issues the node's TLS certificates, so the agent certificate chains to the same trusted CA chain the operations listener uses. Record the certificate subject, expiry date and the CA bundle each node trusts, and add the expiry to your certificate renewal calendar. Keep the private key on the monitoring host with restricted file permissions. The step closes when your verification tooling confirms the client certificate chains to each node's trusted CA bundle and is within its validity period.
Require mutual TLS on operations endpoints
Configure the operations service to require client certificates so that a connection without a trusted certificate fails during the TLS handshake. Apply the setting to each peer and orderer and restart them in one change window so no node is left half-configured. From the monitoring host, request /healthz with the monitoring certificate, then request it again from a host without a client certificate and record both results. The step closes when the unauthenticated request is rejected on every node while the authenticated request succeeds.
Configure the metrics pipeline
Choose Prometheus scraping or StatsD push based on what your monitoring stack already runs. For Prometheus, add a scrape job per node that presents the monitoring client certificate to the /metrics endpoint. For StatsD, point each node's metrics provider at your collector and confirm the collector accepts the metric prefixes Fabric emits. Store scrape intervals and retention settings in version control with the rest of the monitoring configuration. Confirm the exact TLS fields for your Prometheus version in its documentation. The step closes when every target shows as up and an authenticated scrape returns Fabric metric series for each node.
Set the default log level
Set the default logging level in each node's configuration to the level your team agreed for steady state, and record it in the operations runbook. Then test a runtime change through /logspec: read the current specification with an authenticated request, change the level for a single module on one peer, and reset it afterward. Log every runtime change in the incident record, because a debug level left on increases log volume and can expose operational detail. The step closes when the endpoint returns the active specification and the changed level takes effect on that node without a restart.
Confirm health and version over mutual TLS
From the monitoring host, request /healthz and /version on every peer and orderer using the monitoring certificate, and compare each reported version with the release you deployed. Write the results into the baseline so a later upgrade can be checked against it. Add an external health probe for each node that uses the same certificate, so alerting fails along the same path operators use. Expect version differences on nodes that are mid-way through a rolling upgrade. The step closes when every node returns a healthy status and a version that matches its deployed binaries, and the check runs from the monitoring host on a schedule.
Which alerts catch ordering, endorsement and gossip problems early
The alerts that matter most on a Fabric network fall into three families: ordering, endorsement and gossip. Each family has a different failure pattern, so each needs its own signals rather than a single generic node-down check. Start by wiring alerts for the ordering service, because it is the component whose loss stops every channel at once. Fabric's documentation recommends the Raft ordering service, which is crash fault tolerant and keeps working while a majority of its nodes are available. A five node Raft cluster tolerates the loss of two nodes. That arithmetic makes the first alert that matters the one that fires when the cluster loses its second node, because the third loss removes quorum and halts block creation. Treat that state as a page-now event, not a ticket.
Ordering problems show up in three forms: lost Raft quorum, orderers that cannot reach each other, and a node falling behind the cluster. Watch the orderer operations endpoints for health check failures, and alert on the Raft-related metrics that report leader changes, the current leader, and each node's block height relative to the others. Frequent leader elections or a node whose height stays behind the rest usually point to network partitions, overloaded hosts, or disks that cannot keep up, and they deserve an alert before quorum is at risk. Confirm the exact metric names against your Fabric version and the operations service documentation, since names and labels differ between the 2.5 LTS and 3.x lines.
Endorsement problems appear as peers that fail to respond or respond slowly to proposal requests, and as chaincode errors that surface in peer logs. Alert on endorsement request failures and on latency percentiles for endorsement calls, measured against a baseline you collect during normal load. Chaincode container errors are a separate signal. They often appear in peer logs before they reach client applications, so a log-based alert on chaincode launch or execution errors gives the earliest warning. Log levels can be raised at runtime through the /logspec endpoint to gather detail during an incident, but leave them at a normal level in steady state so that log volume does not hide the signal you are watching for.
Gossip problems are the quietest failure. A peer that stops receiving blocks, or that diverges in height from other peers in the same channel, can keep answering health checks while it silently falls out of step. Alert on the gap between each peer's ledger height and the highest height in its channel, and alert when that gap persists beyond a short window you set from your own baseline. Pair this with a check that every peer in each organization is still receiving new blocks. Thresholds for all of these alerts should come from the observed behavior of your own network under expected load, not from figures copied from another deployment. Ask your platform team to record a baseline for at least one full business cycle before setting paging thresholds, and review them after each upgrade or topology change.
What drives monitoring effort and how to keep it down
This table lists the factors that most change the effort of monitoring a Fabric network through the operations service, along with ways to reduce each one. Read each row as a lever: the first column names the driver, the second explains why it moves effort, and the third gives the reduction to request or build. The main takeaway is that repeatable configuration and one certificate process usually matter more than the raw node count.
| Driver | Why it changes effort | How to keep it down |
|---|---|---|
| Number of organizations and nodes | Each peer and orderer exposes its own operations endpoint, so every node adds a scrape target, a health check and a log level to manage. | Generate scrape configuration from one inventory file per node type, and keep peers and orderers on the same naming and port pattern. |
| Number of environments | Development, test and production each need their own endpoints, certificates, dashboards and alert thresholds, and drift between them creates false alarms. | Keep one versioned monitoring template and vary only environment values such as hostnames, thresholds and certificate paths. |
| Existing monitoring stacks in each organization | If an organization already runs Prometheus or a StatsD collector that can reach Fabric endpoints, integration is short. If not, network paths, firewall rules and agents must be built first. | Ask each member to confirm, before design, whether its stack can reach the operations ports over mutual TLS, and agree one shared scrape pattern or push target. |
| Certificate management for mutual TLS | The operations service requires client certificates, so each scraper or collector needs a valid identity that is issued, renewed and revoked alongside the network's other certificates. | Use one certificate issuance process for monitoring clients, with a single owner, a documented renewal date and the same CA chain used for the network. |
| Alert tuning time | Sensible thresholds for ordering latency, endorsement failures and gossip health depend on observed baseline behavior, which new networks do not yet have. | Start with a small set of alerts on health checks and error counts, record a baseline under normal load, then tighten thresholds in a planned review. |
| Dashboard creation | Each organization may ask for different views, and hand-built dashboards are costly to update after upgrades change metric names or labels. | Template a common dashboard for peers and orderers, and limit per-organization customization to panels that are truly local. |
| Runtime log level changes | Raising log levels through the logspec endpoint during an incident is useful, but unmanaged changes across many nodes are hard to track and reverse. | Document who may change log levels, require the change to be recorded, and set a reminder to restore the default level after the investigation. |
| Upgrades and version changes | Metric names, labels and endpoint behavior can shift between Fabric releases, so dashboards and alerts may need checking after each rolling upgrade. | Add a metric and alert check to each upgrade runbook, and confirm the target release notes before changing the monitoring templates. |
What to keep running after go-live
Monitoring stays useful only if someone keeps it accurate after go-live. Four recurring duties sit with the operations side of a Fabric network: reviewing alert rules when the Fabric version changes, rotating the client certificates the monitoring agent uses before they expire, applying the retention policy to logs and metrics, and keeping a record of runtime log level changes so verbose logging does not stay on. Each member organization should name an owner for every duty in its runbook, and that owner should hold access to the peer and orderer hosts as well as the dashboards, since changing a log level or rotating a certificate requires host-level action.
Ownership follows the same lines as the network itself. Each member organization owns the operations endpoints on its own peers and orderers, the metrics and logs those nodes produce, the retention period applied to them, and the certificates its monitoring agent presents over mutual TLS. The consortium should agree on what it shares: a common metric naming scheme, a shared set of ordering and endorsement alert thresholds, a calendar of Fabric version changes, and the upgrade windows in which dashboards are checked. Write these shared items into the governance agreement so that no member has to guess which thresholds the others apply.
Dashboards need a review after each upgrade step, not only after the first. The Fabric upgrade guide runs in four steps: back up the ledger and MSPs, upgrade orderer binaries in a rolling fashion, upgrade peer binaries in a rolling fashion, then update application channel capability levels. After each rolling step, confirm that Prometheus scrapes or StatsD pushes still arrive, that the /version endpoint reports the expected build on each node, and that /healthz returns healthy. Check the release notes for the version you are moving to to see whether any metric names changed, because alert rules that reference renamed series tend to fail silently.
Monitoring does not replace backups. The ledger and MSP backups described in the upgrade guide are a separate step that must finish before any binary changes, and a healthy /healthz response does not show that a ledger can be restored. Keep backup jobs, restore tests and their schedules in the operations runbook, each with a named owner. If an alert and a backup disagree about a node's state, treat the most recent verified backup as the recovery point and investigate the alert on its own track.
How to lock down the operations service
Five controls turn the operations service from a convenient diagnostic tool into a protected endpoint. Each one closes a different path: network reach, client authentication, runtime configuration, scraper connectivity and credential handling. Work through them on every peer and orderer before alerting goes live.
Keep the operations service off public networks
The operations service listens on its own address and port, separate from the peer or orderer endpoints that serve transactions. Bind it to an internal interface or management network, and confirm the listen address setting for your Fabric version. Then test from outside that segment to confirm the port is unreachable, including through load balancers, container port publishing and cloud security groups. A health or metrics endpoint exposed to the internet reveals node state and version details to anyone who can reach it.
Require mutual TLS on every endpoint
The operations service is secured with mutual TLS client certificates, and that requirement should cover every route: /healthz, /logspec, /version and /metrics. Confirm in your version's documentation that client authentication is enabled and that the trusted CA list contains only the operations CAs you intend. Test each endpoint without a client certificate and expect a refusal. Repeat this check after every certificate renewal, because a rotated CA can silently break scrapers.
Limit who can change log levels
The /logspec endpoint changes logging at runtime, which can expose sensitive transaction detail or fill disks with debug output. Restrict it to a small set of named operator identities. Confirm how your version decides which client certificate may call it, and keep scraper certificates out of that set. Record each log level change in your change process, and return debug levels to their defaults after troubleshooting.
Restrict scraper network paths between organizations
Prometheus or a StatsD collector needs a network path to each peer and orderer operations port, and in a consortium those paths cross organization boundaries. Allow only the collector's address range through each organization's firewall, and avoid a shared flat network across members. Document which collector reaches which node. Review these rules whenever a member adds nodes or changes monitoring hosts, and keep alert delivery inside the same controlled path.
Store monitoring keys in a controlled store
The monitoring agent presents a client certificate, and its private key grants access to every operations endpoint it can reach. Fabric's HSM guidance keeps TLS keys file-based, so store this key as a file readable only by a dedicated service account, with strict permissions. Keep it out of container images and shared configuration repositories. Confirm with your secrets policy how the file is provisioned, rotated and audited, and whether disk encryption covers the host.
How to test that monitoring works and fails safely
A monitoring setup is verified only when a known failure produces the expected signal. Before go-live, write the verification plan as a short list of tests, each with the result you expect recorded in advance. Run the tests in a test network that matches the production topology as closely as your environment allows, using the same operations service settings, the same TLS configuration and the same Prometheus or StatsD pipeline. A dashboard that shows green on a healthy network proves very little. The plan needs to show that the alerts fire when the network is unhealthy, that access controls hold, and that the monitoring system reports its own gaps.
Start with access. From the monitoring host, using its own client certificate, confirm that authenticated requests to the operations service metrics endpoint return data from every peer and every orderer in scope. Then confirm the negative cases: an unauthenticated request should be rejected, and so should a request presenting a certificate issued by a CA the operations service does not trust. Run the same checks against the health and version endpoints, since those are exposed on the same service. Check the TLS settings for the operations service in the Fabric documentation for your exact release line and confirm which certificates are accepted, because a misconfigured trust bundle is one of the most common reasons a scrape silently stops returning data.
Next, test ordering resilience. In the test network, stop one orderer in the Raft cluster and confirm that the alert for that node fires within the time your alerting rules define. At the same time, confirm the expected quorum behavior: if the remaining nodes still form a majority, transactions should continue to be ordered and endorsed. Then run a second case, stopping enough orderers to break the majority, and confirm that the critical alert fires and that ordering halts as the Raft documentation describes. Ask your platform team to state the expected node count for each case before the test, since a five node cluster tolerates the loss of two nodes and any other count changes what the alert should say. Gossip problems deserve the same treatment: block traffic between two peers and confirm the relevant peer metrics change and the alert fires.
Two more tests close the loop. For log levels, change the level on one test peer through the /logspec endpoint, confirm the more verbose output appears, remove the change and confirm the peer returns to its configured level. Verify that the endpoint itself is restricted to the operators you named, because a runtime log level control left open is a diagnostic risk in its own right. For the monitoring system, simulate an unreachable metrics endpoint by blocking the scrape path to one peer. The monitoring system should raise a target-down alert and route it to a named owner. Record each test, its expected and actual result, and the person who signed it off. That record becomes the evidence your auditors and your own operations team will ask for later.
Questions teams ask before setting up Fabric monitoring
Can our own team set up Fabric monitoring without a partner?
Yes, if the team has the right skills and time. The peer and orderer operations service is built into Fabric, so the health, log level, version and metrics endpoints exist once each node is configured. Your team still needs people who understand Fabric node configuration, mutual TLS client certificates, a metrics pipeline (Prometheus or StatsD), dashboards and alert tuning. Most teams underestimate the cross-organization work: each member organization must agree on access, network paths and who responds to alerts. A partner is useful when that coordination is the bottleneck. Confirm your internal capacity against the scope you write down before starting.
What does Fabric monitoring need from our infrastructure team?
Certificates, network paths and decisions. Operations endpoints are secured with mutual TLS, so the collector or scraper needs valid client certificates that the nodes accept. Your network team must open paths from the metrics collector to each peer and orderer operations listener, across any segments or organizational boundaries in between. You also need to decide which metrics pipeline you run, how long metrics are retained, where the collector is hosted, and who owns each alert. Ask your security team to review certificate storage and rotation before the collector goes live, since certificate expiry can silence monitoring at the moment you need it.
How do the number of nodes and organizations change monitoring cost and effort?
Each node and each organization adds work. Every peer and orderer adds an endpoint to scrape or push, a certificate to manage, and configuration to keep consistent. Every member organization adds a trust boundary, with its own certificates, network approvals and alert owners. Cost falls into design, build, dashboards and alert rules, then ongoing operations staff, most of which recurs. Effort rises with the number of environments too, such as development, test and production. When you request a quote, ask for effort broken down by node count, organization count and environment, and confirm which items are one-time and which recur.
How long does Fabric monitoring setup take?
Duration depends on scope, which must be agreed in writing. The main drivers are the number of nodes, whether one organization or a consortium is involved, whether you need a new metrics stack or are extending an existing one, whether dashboards and alert rules are included, and how many environments must be covered. Network and certificate approvals often set the pace more than the configuration itself. Most DeployOpen engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts. A practical approach is to build monitoring in a non-production environment first, then repeat it in production once the alerts have been tested.
Should we use Prometheus or StatsD for Fabric metrics?
Both are supported, and the right choice depends on your existing stack and network rules. Prometheus uses a pull model: the collector scrapes each node's metrics endpoint. This fits teams already running Prometheus, but the collector must reach every node. StatsD uses a push model: nodes send metrics to a receiver. This can suit environments where inbound connections to nodes are restricted, but it requires a receiver and a plan for aggregation and loss handling. Consider who operates the pipeline, what tooling your alerting already uses, and how your firewall rules are written. Confirm the choice with your platform team and check the metrics available in the Fabric version you run.
What should Fabric alerts cover for ordering, endorsement and gossip?
Alerts should track availability of consensus, endorsement and peer connectivity. For ordering, watch whether each orderer is reachable and whether the Raft cluster still has a majority, since a five-node Raft cluster tolerates the loss of two nodes and keeps working only while that majority holds. For endorsement, watch peer health and chaincode-related failures. For gossip, watch peer connectivity and whether peers stop receiving blocks. Thresholds and exact metric names depend on your Fabric version and configuration, so confirm them against the documentation for that release. Agree in advance who receives each alert and what the first response is.
What happens to monitoring during a Fabric upgrade?
Monitoring follows the upgrade node by node. Fabric upgrades roll through orderers first, then peers, so each node restarts in turn and its operations endpoints may be briefly unavailable. Plan alert silences or expected-restart rules for those windows, and make sure alerts for the rest of the network stay active. After each node is upgraded, check its version through the version endpoint to confirm the binary changed as planned. Before upgrading, back up the ledger and MSPs as the upgrade guide describes. Review release notes for changes to metrics or log settings, and keep alert rules in version control so they can be reapplied after changes.
How DeployOpen scopes, builds and hands over Fabric monitoring
DeployOpen scopes Hyperledger Fabric monitoring around the peer and orderer operations service, so health checks, metrics and log levels are defined before anything is wired to alerting. Work is built inside your own infrastructure and agreed in a written, fixed-scope plan. Each engagement ends with documented endpoints, alert rules and a handover your operators can run.
How we scope
- Written scope listing the peers, orderers and organizations whose operations endpoints will be monitored.
- Agreed inventory of which /healthz, /version, /logspec and /metrics endpoints each node exposes.
- Fixed-scope agreement: deliverables and timelines are confirmed in writing before work starts.
How we build
- Operations service enabled with mutual TLS client certificates so only authorized scrapers reach metrics.
- Prometheus scrape configuration or StatsD push, chosen to match your existing monitoring stack.
- Alert rules for ordering, endorsement and gossip problems, tested against controlled failures in a non-production environment.
How we hand over
- Runbook covering each health check, log level change procedure and the response expected for every alert.
- Dashboards and alert definitions exported as configuration your team can version and review.
- Walkthrough with operators covering runtime log levels, so verbosity is raised and reset safely.
How we support
- Optional retainer for ongoing support, covering alert tuning and changes as nodes are added.
- Monitoring checks reviewed during Fabric upgrades, since 3.x and 2.5 LTS patch lines ship in parallel.
- Ask for ongoing support and upgrade work to be itemized in the written scope before signing.

