A modern building generates an enormous number of data points — tens of thousands of sensors, setpoints, and status values across HVAC, metering, and lighting. The problem is rarely getting the data. The problem is that a point called AHU3_SAT means nothing to anything except the person who named it. Project Haystack exists to fix that, and it is quietly one of the most important ideas in building automation. Here is why it matters.
The Problem: Data Without Meaning
Point names in building systems are historically a free-for-all. One integrator writes SAT, another writes SupplyTemp, a third writes AHU_3_DAT — all meaning the supply-air temperature of the same kind of unit. To a person who knows the building, these are readable. To software — analytics, fault detection, a portfolio dashboard — they are opaque. You cannot ask “show me every air handler’s supply temperature across all my buildings” when every building names it differently. That single limitation blocks most of the valuable things you would want to do with the data.
The Fix: Tags That Describe What Things Are
Project Haystack is an open standard for tagging building data with meaning. Instead of relying on a cryptic name, each point gets tags describing what it actually is — that it is a temperature sensor, that it measures supply air, that it belongs to a particular air handler serving a particular zone. The naming can stay whatever it is; the tags give software a consistent, queryable model of what everything means.
Once data is tagged, it becomes addressable by concept rather than by name. “Every supply-air temperature on every air handler” becomes a query that works across buildings, because the meaning — not the label — is what the software reads.
What Semantic Tagging Unlocks
- Analytics that scale. Fault detection and performance rules can be written once against tagged concepts and applied across a whole portfolio, instead of rebuilt per building.
- Faster onboarding. A new building added to a tagged system inherits existing analytics immediately, because its points describe themselves in the same vocabulary.
- Portfolio views. Cross-building comparison — energy, comfort, equipment health — becomes possible because the data finally speaks a common language.
- A foundation for AI. Any serious attempt to apply machine learning or AI to building data depends on that data being structured and labeled. Tagging is the groundwork that makes it feasible.
Where Niagara Fits
The Niagara Framework works naturally with tagging — points carried in a Niagara system can be tagged and organized so the data model is meaningful, not just a flat list. The value, again, is in doing it consistently and thoroughly: a half-tagged system delivers half the benefit, and inconsistent tagging reintroduces the very problem it was meant to solve. Done properly, tagging turns a pile of points into a structured asset you can actually build on.
The Bottom Line
Collecting building data is easy; making it useful is a data-modeling problem, and semantic tagging is the answer. If you have ambitions for analytics, portfolio-wide insight, or AI on your building data, structured tagging is not optional — it is the foundation everything else stands on.
SoftwarePile builds Niagara systems with data structure in mind — tagged, organized, and ready for the analytics you will want next. Tell us what you want to do with your building data and we will help you build the model to support it.
What a Tag Actually Is: Markers, Values, and Refs
A Haystack tag is a name/value pair applied to an entity, and the ones that carry the most meaning carry no value at all. A marker tag such as sensor, supply, air or temp asserts that something is true about the entity, and its presence is the entire statement. Value tags carry typed data instead: unit as a string, tz as a time zone name, curVal as a number with a unit, siteRef as a pointer to another entity. Filters are written against that vocabulary, so point and sensor and supply and air and temp selects the same thing in every building that uses the vocabulary correctly.
Three structural entity types do most of the work. A site is a building, with a time zone and an address. An equip is a piece of equipment. A point is a single readable or writable value. Equip entities carry siteRef, points carry equipRef, and the filter language walks those pointers with the -> operator, so point and equipRef->ahu reaches every point on every air handler without naming a single one of them. That reference graph, not the tag list, is what makes a portfolio query possible. Tags with no refs give you a bag of correctly labeled values with nothing to say which building or which unit they came from.
A point also has to declare how it can be reached, and the three ways are independent of each other. Code written on the assumption that every point offers all three is one of the more common reasons a working model returns nothing useful.
- cur: the point exposes a live current value, read through the API or a watch.
- his: the point is historized and can answer a hisRead over a time range. A point can be marked his and still have no samples.
- writable: the point accepts a commanded value at a priority level, which is a different operation from writing a tag.
- kind on a point is Number, Bool or Str. A Number point with no unit tag is ambiguous to any rule that compares it against a threshold.
- tz belongs on the site and on any historized point, and every timestamp in a hisRead response is interpreted against it.
Tagging Inside Niagara: Namespaces, Dictionaries, and Relations
Niagara does not carry bare tags. Every tag lives in a namespace, so you are working with n: tags from the Niagara dictionary and hs: tags from the Haystack dictionary, and the namespace is part of the name in every query you write. This matters the first time a query silently returns nothing because it asked for a bare marker instead of the namespaced one.
Tags arrive in a station two ways. Implied tags come from a tag dictionary, whose rules assign tags automatically based on component type, so every NumericWritable of a given type inherits the same markers with no per-point work. Direct tags are applied to one component and are for everything the rules cannot infer. The split is worth planning around: the type-driven half of the model is cheap and consistent, and the expensive half is always the same half, which is saying which equipment a point belongs to and which equipment belongs to which site.
Relations are a separate mechanism from tags, and confusing the two costs time. A tag describes what an entity is. A relation is a typed, directional link between two entities, and the Haystack parent references are carried as relations rather than as ordinary tag values. Querying reflects that split as well. NEQL queries the tag and relation model, while BQL queries the component space by type and path. A BQL report bound to a folder path keeps working until someone reorganizes the tree, and an NEQL query survives the reorganization but returns nothing if the tags were never applied. Knowing which one a report is built on tells you in advance what will break it.
How Tag Models Break, and How the Breakage Shows Up
Haystack filters are conjunctions, and a missing marker does not produce an error. It removes a row. That is the actual mechanism behind an incomplete model: the query returns a smaller answer that looks entirely plausible, and any average, count or rollup computed on top of it is wrong in a direction nobody can see from the result. The diagnostic is arithmetic. Run the filter, then run it again with one term dropped at a time, and compare the counts against the equipment schedule for the building. The term whose removal changes the count is the term that is missing from the model.
Over-tagging fails in the opposite direction and is harder to notice. Markers added for completeness to points that do not deserve them make those rows match filters they were never meant to match, so a fault rule evaluates a point that is not the thing the rule assumes it is. Nothing errors. The rule simply produces a fault, or suppresses one, on the wrong equipment, and the model looks more complete than the one that is quietly missing rows.
Units and time zones are the two that survive review longest, because both produce numbers that are inside the range a rule expects. A Number point tagged as degrees Fahrenheit while the driver delivers Celsius reads low but reads plausibly, so no threshold ever trips. A historized point with a tz that differs from its site shifts every hisRead by a fixed number of hours, which surfaces as a schedule or occupancy fault that appears and disappears with daylight saving rather than with anything happening in the building.
- Dangling equipRef: the point exists and matches a flat filter, but the referenced id no longer resolves, so any filter using -> returns nothing.
- Equip with no siteRef: the equipment and its points work perfectly in a single building view and are invisible to every portfolio query.
- Synonym drift across buildings: discharge in one model and supply in another for the same measurement, so a filter that is correct in one site is empty in the next.
- Number point with no unit: comparisons still evaluate, and the threshold means whatever the underlying driver decided it means.
- Point marked his that was never actually historized: matched by every discovery query, empty on every hisRead.
Getting the Model Out: the Haystack HTTP API
Tagging a station and serving a tagged model are two different jobs. The Haystack REST API defines a small, fixed set of operations reached under a project path: about and ops and formats for discovery, read and nav for the model, hisRead and hisWrite for history, pointWrite for commands, watchSub, watchPoll and watchUnsub for subscriptions, and invokeAction. Every request and every response is a grid, meaning rows, columns and grid level metadata, encoded as Zinc under text/zinc, or as JSON or CSV according to content negotiation. A client that can parse one encoding and one grid shape can call every operation the server exposes.
Two behaviors of that API catch people out. Errors are returned as grids, with an err marker and a description in the grid metadata, which means a failed operation can arrive with an HTTP 200 and a client that only inspects the status code will read a failure as an empty result. Authentication is the other one: the 3.0 spec defines a pluggable HTTP authentication framework whose standard scheme is a SCRAM handshake over the normal challenge and authorization headers, so the first exchange is a multi message negotiation rather than a single credential header. Watches add a third: a subscription carries a lease, the client is expected to poll before the lease expires, and a watch that lapses stops delivering updates without anything on the client side reporting a problem.
The first thing anyone runs against a new endpoint is a read with a filter. When that comes back empty, or comes back with rows nobody expected, there are four distinct failures hiding behind one grid, and testing them in this order tells you which one you have.
- Vocabulary problem. Read a handful of entities with a bare point filter and look at the tags that are actually present. If the model uses different markers, or namespaced ones, the filter was never going to match and nothing downstream is wrong.
- Broken ref chain. Run the filter without the -> traversal. If the flat version returns rows and the traversal version does not, the tags are fine and equipRef or siteRef is missing or pointing at an id that no longer resolves.
- hisRead returning an empty grid on a point that matched. Confirm the point carries his, then widen the range and check the tz on the point against its site, because a range expressed in the wrong zone can miss real samples by hours.
- Rows for the wrong equipment. Here the tags are right and the references point at the wrong parent. Compare equipRef and its display name against the panel or the mechanical schedule, since nothing in the point data itself will contradict a confidently wrong relation.
Haystack 4 Defs, Xeto, and the Neighboring Models
Version matters more than it looks. Haystack 3 vocabulary was convention: documented combinations of markers, enforced by nothing but discipline. Haystack 4 replaced that with defs, where every term is a formal definition with declared subtyping, so a tool can reason that an air handler is a kind of air handling equipment instead of string matching a marker. It also introduced conjuncts, which fold a combination of terms into one, and that is exactly where a 3 era model and a 4 era tool disagree. The older model carries the loose markers, the newer tool asks for the conjunct, and the disagreement presents as missing data rather than as a version problem, which sends people looking at the wrong layer.
Xeto adds an explicit specification layer above the tags: you declare what an instance of a given equipment type must contain, then validate a real model against that declaration. The practical effect is to move a question like whether anyone tagged the return fan out of discovery, where you find out when a query comes back short, and into validation, where you find out immediately and with a specific answer.
Haystack is not the only model with this goal, and the choice is worth making before tagging rather than after. Brick expresses the same problem as an RDF ontology, queried with SPARQL and built around formally typed relationships, and ASHRAE 223 approaches it from the standards side. The deciding question is what consumes the model. Building analytics and fault detection products overwhelmingly read Haystack tags, so a model built for them should be a Haystack model. Graph and research tooling is more likely to want RDF. Either target is reachable, but retagging a portfolio after the fact is the expensive half of the work, so the consumer belongs in the decision at the start.
Frequently Asked Questions
Is Project Haystack a protocol like BACnet or Modbus?
No. Haystack is a data model plus an HTTP API for exchanging data that has already been acquired. It does not poll a controller, does not replace BACnet/IP, Modbus or any other field protocol, and does not change how a point gets into the station. The drivers still do the acquisition, and Haystack describes what was acquired so that other software can find it by meaning instead of by address or name.
What has to already exist before a model can be tagged?
The points have to be live in the station first, because you cannot tag a point that has not been integrated. Beyond that you need an equipment list that matches what is actually installed, and a decision about where site boundaries fall when a campus or a multi-tenant property is involved. The part that cannot be derived from the data is the parent relationships, which is to say which unit serves which zone and which meter covers which area. Someone who knows the building has to supply that, and it is usually the longest lead item in the work.
Do we have to rename existing points to tag them?
No, and renaming is usually the wrong move. Tags sit alongside the existing name, which is the whole design intent of the model. Renaming points on a live station breaks graphics bindings, alarm and schedule links, history identity, and any report bound to a path, so it creates a large amount of work that tagging exists specifically to avoid.
How does tagging actually proceed on an existing station?
The usual order is namespace and tag dictionary first, then the site and equip entities that everything else will hang from, then implied tags applied by component type in bulk, then direct tags and relations for what type rules cannot infer. Validation is the last step and it is done by query, not by inspection: run the filters the model exists to serve and check the row counts against what the building should return. The bulk work goes quickly because it is type driven, and the hand work is almost entirely the parent relationships.
What does the customer have to decide?
The query set, meaning the specific questions the model has to answer. That determines how deep the vocabulary goes and, more usefully, where tagging can stop, since a model tagged to answer a fixed set of rules is finishable and a model tagged for completeness is not. The second decision is whether the model will be served outside the station and to what, because that sets the encoding, the authentication and the operations that have to be exposed.
Does tagging add load to the station at runtime?
Tags and relations are metadata attached to the entity model, not logic evaluated on every scan, so the control side is unaffected. The cost appears at query time instead. A filter with deep reference traversal across a large station is not free, and a dashboard polling it every few seconds is a materially different load from the same query run once a night, which is worth knowing before a portfolio view is pointed at a controller rather than at a supervisor.
Can analytics run without any tagging at all?
Yes, for one building with a fixed set of rules and a hand built point map. That is a legitimate option when the scope is genuinely fixed, and it is how a great deal of building analytics was originally deployed. It stops working when the second building arrives, because the mapping is not reusable and has to be rebuilt by hand for every site and re-checked after every controller change.