On this page · 21 sections

1.19 (unreleased)

The station does things on its own, and remembers what it did - and the designer looks like a tool for drawing plant screens.

  • Alarms, as a table. A fourth view beside Panel, Diagram and System: one condition on one tag per row - above or below a limit, equal or not equal to a value, on, off, or not delivering - with a deadband, an on-delay, a priority from 1 (critical) to 4 (low), an area and the words the operator reads. The station evaluates the table of every app it serves from the moment it is published, whether or not a screen is open, keeps the ISA-18.2 states (a condition that cleared before anybody responded stays on the summary until somebody does), and writes every raise, clear, acknowledgement and shelving to alarms.jsonl and the acknowledgements to the audit trail with the user. The designer evaluates the same table as it is written, so a row shows its alarm firing; a tag dropped on the table is an alarm, and a spreadsheet of them comes in and goes out as CSV. An Alarm grid or banner placed from the palette starts bound to alarms.summary, and what an operator does on it goes back to the station. Before this an alarm needed a flow, and nothing listened when an operator pressed Acknowledge. See Alarms.
  • Alarm actions. An alarm grid reports what an operator asks for and has no buttons of its own, so on a published screen nothing acknowledged or shelved from it. The Alarm actions block is those buttons, and a grid placed from the palette shows its row checkboxes. Found on the way: the grid's getSelection() answers the table's row keys, not alarm ids, so the component's own demo acknowledged nothing when a row was ticked - the grid gained selectedAlarms().
  • The station's users, managed from the designer. System › Users lists each station's users with their roles, its tokens and its identity provider. An engineer adds a user, changes a role, sets a password or removes one there, and the station takes it at once - no restart - signing out a user whose role or password changed. The last engineer cannot be demoted or removed, nobody removes themselves, and a station without sign-in still makes its first engineer on the station itself. A signed-in user can change their own password over the API (POST /api/me/password); there is no screen for it yet. Every change is in the audit trail. Before this, a user was a command on the station and a restart.
  • A first run for the people who buy a SCADA tool. The designer opens on the plant overview and a gallery of eight templates, each shown running - plant overview, alarm management, line operations, andon board, KPI wall, safety interlocks, meter logger, signal generator - instead of the signal generator and a paragraph about typed wires. Project › New from a template… brings the gallery back.
  • The component library calls nowhere. The Smart UI framework no longer posts the licence key to jqwidgets.com (it did, at most once a day, when its cached answer had grown old); the key is checked on the device. A station on an air-gapped network runs licensed exactly as a connected one.
  • Fixed: a diagram on the station saw only the first value of a station tag. The station's own subscribers - the diagrams that run there, and now the alarms - were dropped by the sample broadcast the first time a value changed, because they did not say they were open; they would also not have read a turn's samples arriving as one array. Both mended.
  • Fixed: an operator could not do what the operator routes allow. The API gate asked for an engineer on every request that was not a read or a tag write, so an operator applying a recipe, running a job by hand, writing to the shift log, classifying a stop or moving an order on was refused before the route could say yes. The gate now lets the operator routes through, and each route still checks the role itself.
  • Fixed: a restart of the station emptied the alarm summary. An alarm nobody had acknowledged - still active, or returned while unattended - was gone after a restart, and so were shelving and acknowledgements. The station now keeps its outstanding alarms beside its configuration as they change (alarms-state.json) and takes them back when each app starts; the first value decides what is still active, so one that returned while the station was down is cleared then and stays until somebody acknowledges it. Found by an operator walk-through.
  • Fixed: the touch keyboard typed a wrong setpoint. On a number box a touch put the caret before the right-aligned value, so "87" went in ahead of it ("8789.9"); Enter did not commit it, and when it was committed later the box clamped it to its maximum, which was written to the PLC. A number is now typed over - the first key replaces it - with the keypad and the range; a number outside the range is refused with the reason; Enter commits at once. In the sign-in dialog the docked keyboard covered Password, a touch meant for it took the focus from Name, and the name came out backwards; the keyboard now lifts the dialog above its keys and keeps the caret. Its Enter in Password now signs in, as a keyboard's does.
  • Fixed: an alarm count went on reading 0 while the station was down. Every other cell went stale; a tile bound to alarms.active did not, because the page's alarm source was always "open". It now follows the station's connection.
  • The documentation shows what it describes. Sixteen pictures taken from the designer and the apps as they are - the designer, the first run, a source, bindings, the Project menu, the touch keyboard, the System view's Live and Users tabs, the Alarms view, a diagram, a message flow, publishing, the agent's home page, a published screen, the alarm screen and a wall in kiosk mode - by scripts/build-studio-doc-images.js, so they are taken again when a view changes rather than drifting. Taking them found five faults, all mended: the Alarms view opened from a link or a reload read every alarm "normal" with its tag far over the limit (its clock never started); ?mode= lost to the view last used; the password fields of Publish, a source and a user stayed white on the dark theme; the first-run gallery's three steps touched the cards. The touch keyboard named a number box "Numeric Text Box"; it names it by its label now.
  • Siemens S7 and EtherNet/IP are in the documentation. Siemens S7 was in the station and in none of the pages a buyer reads, and EtherNet/IP only on the agent page: the source reference, the agent page (a new Siemens S7 section - the addressing, rack and slot, and the PUT/GET and non-optimised data block a 1200 or 1500 needs), the home page, the Sources dialog and the site. The docs check now compares the source reference with the station's own table of drivers, so the next one cannot be missed. Also: code examples written indented had come out as one run-on paragraph; the component count is read from the component list; a page's On this page list folds on a phone.
  • The plant overview sample tells the truth about its alarms. Its banner held two alarms written into the file and its Active alarms tile a number a diagram set to 2 or 3; both now read five real alarms on the sample's tags.
  • The designer, put in front of five people trying to break it. Five test passes - editing a panel, sources and bindings, diagrams, a project's life from import to publish, and a long run of random clicks and keys - found about sixty faults. The ones that could reach a plant: a number shown in another unit (°F from a °C tag) was written back unconverted, so 150 °F went to the PLC as 150 °C - a write now converts back, after rounding to the step in the unit shown; a binding to a tag no source carries showed made-up simulator values - it now says no source carries this tag; a pasted token was sent to every station published to - each station now keeps its own, and only once it has taken a publish; a component on the panel took the clicks meant for selecting it, so a button was pressed or a slider moved while editing - it answers only in Operate; a second tab saved over the first one's work without a word - it now says so and keeps its own only when told to; two frames on a diagram could each hold the other, and the designer froze.
  • One more pass over the designer, and over undo. Four more test passes - the panel and its themes, sources and alarms, a project's life, and every kind of edit undone and redone - found these, all fixed. What could reach a plant: a binding re-pointed from a °C tag to a % tag kept the °C and °F, and 50 typed on the screen wrote 10 - the units now follow the new tag, and it says so; a slider shown in another unit kept its range in the tag's unit, and one click wrote 7.25 °C into a setpoint at 84.6 - its range converts with its value, and a drag writes a multiple of its step, not 79.95702743530273; an alarm row the table marked red was evaluated anyway (an unknown condition as above, a limit written as a word latched on "over NaN", a tag no source carries raised from a made-up signal) - such a row is now not watched; an alarm switched off while it stood stayed on the operator's banner for ever - it leaves the summary; not delivering never fired in the designer, Preview or an exported app when a link dropped - it does after five seconds; Simulate it there replaced the station's own simulator and rewrote its configuration without asking - it asks. What could lose work: Edit this screen and a ?project= address replaced the browser's work without a question, and Preview could show another tab's project. A page's name was written into the app's tabs as markup, so a name could run a script in Preview and in an exported app; it is text now. Undo: after a field had been left, Ctrl+Z undid the browser's typing instead of the edit, and in a drop-down, a checkbox or the Alarms view it did nothing; the first control of a project, a project's name and its theme took the wrong number of steps; a drag through the colour picker made one step per colour; a click that changed nothing emptied Redo; an undo on another page or diagram happened out of sight - it now shows where it happened and selects what came back. And: a paste could give two components one id; with Operate ticked the arrows moved a slider and its component; a cut and paste lost the diagram terminal, and the knob, the bar graph and the graphs got none; on the Industrial theme a gauge's scale and a knob's track were the canvas's own grey; a status message was replaced by Saved within half a second (it stays six now); the System view waited for its slowest station before showing any; a signed-in System view left the project's station sources unsigned, and such a source now says not signed in instead of showing green; the sign-in dialog painted the touch keyboard's function keys white on white; the canvas language went back to English on a reload.
  • Light and dark, everywhere. Project › Theme switched the canvas, the panes and the dialogs, and left the top bar dark on every theme and the palette's control pictures white on the dark one; both follow the theme now, and the palette's component pictures come in both themes. The documentation's pictures are taken in both themes too - all but the station's home page, which has one look - and its light/dark switch shows the one that matches.
  • What the documentation promised, checked. Every sentence of these notes and the pages was checked against the code. Where the code was behind the words, the code moved: a Cards block switched pages only on a published screen, and now does in Preview and inside a page that embeds the app; a Siemens S7 source said "closed" when the PLC refused the connection, and now says why, with the rack and slot it tried.
  • And the rest of that list. Re-pointing a binding keeps its value map (and its units when they are the new tag's too), and the Bindings tab edits both; a source may not be called alarms; a project file that is not a project is refused before it replaces anything; a CSV of alarms updates the rows it names and reads a condition or a priority as a sheet writes it (High, >, critical); placing an alarm summary and its buttons is one undo; the System view gives up on a station after five seconds and can remove one; a copied or pasted control gets its diagram terminal; a colour the browser cannot read is refused; long names wrap in the inspector; the status bar wraps rather than hiding; a wire under a frame can be picked; a diagram of 800 nodes draws its wires fifteen times faster (1.9 s to 0.12 s); a job whose action fails answers HTTP 422, not 500 (a 500 said the station was broken); an app's name is at most 64 characters, the longest a station takes; an exported app carries the language packs ?lang= asks for; and the e-signature's refusal no longer runs its reason into the attempts left.
  • The designer, denser. The palette was two large pictures to a row, about ten of forty-five components on screen; it is four to a row now, with the name in the tooltip it already carried. The toolbar's words became icon buttons, which turned up a Redo that had never had one - the editor has been able to redo since the beginning, by a keyboard shortcut nobody was told about. Both arrows now grey out when their stack is empty. The project pane opens at the height its tree needs, up to 420 pixels and 45% of the column, instead of a fixed 220 pixels that cut a row in half on most projects. And a ruler over the canvas, marked in columns, which is the unit a grid is placed against.
  • Shapes, and the tag that colours one. Twenty-two figures - rectangle, circle, diamond, hexagon, arrows, cylinder, line and the rest - drawn as SVG so they stay sharp, stroke cleanly and rotate. Each carries rules: colours by value, tried in order against a bound tag, so a rectangle that turns red over 80 is two fields and no diagram. A colour strip appears under the canvas while a shape is selected - click fills, shift-click outlines - and it leads with fixed state colours close to the Industrial theme's (running green, manual blue, a red, an amber), because on an ISA-101 screen red means an alarm and a palette that opened with a rainbow would invite somebody to paint a pump red.
  • The thirty-three process symbols are placeable. Pump, valve, motor, vessel, exchanger, six kinds of valve, column, reactor, drum, silo, cyclone, turbine, generator, agitator, conveyor, transformer, disconnect, orifice, heater, cooler, breaker, fan, compressor, filter, positive-displacement and vacuum pumps, blower, instrument - all already drawn, and until now reachable only by writing a mimic's layout by hand. They are equipment, and equipment belongs on a panel too.
  • A block can follow a tag. The runtime bound components and skipped blocks, which was right while every block was decoration. A Shape is not decoration, so a bound block's element is given a real accessor for each bound property: Connect writes it as it does on any component, and the block re-applies itself. A heading that shows the running batch number works for the same reason.
  • Recipes. A named set of values written into the plant in one action - "Product A" is 180 °C and 42 bar - so an operator picks a product instead of typing six numbers into six fields and getting the fifth wrong. Written under System › Operations or over /api/recipes, checked where they are written rather than where they are loaded, and every application in the audit trail with the name of whoever asked. A recipe that half-applies answers 207 and names the half that did not.
  • Jobs: the clock as an actor. A night setback at ten, a pump exercised every Sunday, a report on the hour. A job loads a recipe, writes one value or runs a diagram, on a daily, weekly, interval or cron schedule. It does not run late, does not overlap itself, and does not wait for ever - a diagram that has not finished is stopped and the job says so, because a diagram that never finishes is a service and belongs in the app. The designer's Job dialog reads a cron expression as it is typed and says what it will do, using the same reader (studio-cron.js) the station fires on, so the two cannot disagree.
  • The historian. A station keeps what its tags were, in fixed 17-byte records, one file per tag, with no dependency: "history": { "retentionDays": 30, "deadband": 0.2 }. A trend bound to an agent source fills its window - the last 15 minutes by default (timeSpan) - from the historian instead of starting empty. Asked for fewer points than it has, the range is bucketed and each bucket gives up its lowest and highest, so the count is bounded and the spikes survive.
  • EtherNet/IP, for Allen-Bradley. A ControlLogix, CompactLogix or Micro800 addressed by tag name - Tank_Temperature, Motor_1.Running, Flow[2], Program:MainProgram.Cycles - over CIP: RegisterSession, the Unconnected Send carrying the backplane route to the slot, Read Tag, Write Tag, and the Multiple Service Packet so forty tags cost four requests a poll at the default batchSize of 12 (one with batchSize 40 or more) rather than forty. BOOL through LREAL. Hand-written, no dependency, and proved against an emulator written for it, since there is no Rockwell hardware here. That is the larger of the two protocol families the competitor report named as the remaining gap; BACnet remains.
  • Siemens S7, hand-written and without a dependency: TPKT over COTP over S7, DB1.DBD0, M0.0, MW20, I0.7, Q0.1 and the rest, bool through real, reads packed to the negotiated PDU so seven tags are one request, and writes that land in the PLC. Proved against an emulator written for the purpose; there is no Siemens here and the hardware page says so.
  • Pictures, cameras, pages and cards on a screen. Four blocks: an image, a camera that refreshes a still on an interval, another page in a frame, and Cards - this project's pages as tiles to press, which is the landing screen a plant with more screens than fit along a page strip wants on a panel PC operated with a glove. A card raises studio-open-page rather than switching pages itself, so it works the same on a published screen, in a preview and inside somebody else's page through the embed SDK. All four are now in the check suite, which none of them was.
  • The mimic is edited in place. A process graphic is built by dropping symbols onto it and dragging them, rather than by hand-writing the layout.
  • A tag not declared writable is now refused over Modbus too. OPC UA, MQTT, HTTP, SCPI, S7 and EtherNet/IP already refused one (the simulator and DAQmx still go by the tag's kind); Modbus refused only what the protocol itself cannot write, so a holding register left out of the writable list was written anyway. That a register can be written is a fact about Modbus; that it should be is a decision somebody made in the configuration.
  • A crashing check suite says what it had proved. Both suites printed their tally only at the very end, so anything that threw - a browser whose frame detached while the previous run's Chrome was still shutting down, a port not yet released - lost every result and printed a stack. Worse, a reader could not tell a crash from a total failure: "0 ok, 0 fail" looks the same either way. The tally is now printed from one place that runs whichever way the suite leaves, and a crash reports itself as one, with what had passed before it, and exits non-zero.
  • The check numbers mean something. check-agent.js gave 74 and 84 of 86 on two runs of the same tree, and a third crashed after 77 passes, because it waited for the port and then for the sources to be open - and open is not delivered: an OPC UA session is open before the server has pushed the first value of each monitored item. It now waits for the values to settle, and gives the same number twice running.

1.18 (2026-09-22)

The Enterprise proof: what a regulated site and a support contract ask for.

  • Electronic signatures on writes. A write can require one (requires an electronic signature under the write, on the Events tab): the signing panel with the record, the user id and the password verified at the agent, the meaning and the reason, the write over the API with the signature, the agent's check that the session is the signer's, the trail entry with the signature. POST /api/tags/write is the route, for scripts too.
  • The validation pack. npm run studio:validation writes the IQ (with what the script can verify filled in), the OQ from the check suites, a PQ template, the traceability matrix over 35 requirements, the records statement, the raw evidence, as Markdown, JSON, one HTML page and a PDF. The guide.
  • Long-term support. The support page: the tiers with response times by severity, the LTS lines with 24 months of security fixes as patch releases, and npm run studio:lts:check, which names any fix on the main line that has not reached a supported branch.
  • The integrator programme on its own page; the release procedure rewritten around the signed download, the validation pack and the LTS lines.
  • Measured, and faster. npm run agent:bench measures the agent and writes the performance page with the machine named. What it found was fixed: samples are now gathered per client and sent as one frame per turn of the event loop (the runtime always took arrays), which on the bench machine took delivery from 205,000 to 365,000 samples a second at a quarter of the CPU, a write's 95th percentile from 207 ms to 1.3 ms and /api/tags with 2,400 tags from 200 ms to 16 ms; a Modbus poll that is still running is not started again (the queue no longer grows on a slow device; stats.overruns counts it). /metrics gained the process's CPU, memory and event-loop lag.
  • The designer measured too: a page of 300 bound components opens in about a second in the designer and the app, a 300-node diagram draws in under one - on the performance page, with the machine. And an agent restart under an open screen is now a check: the screen says so, reconnects with its session, shows live data again.
  • A page inside a page, the canvas at the screen's size, and one search for everything. The Page block renders another page of the project inside a cell, live, with a guard against a page holding itself. A width beside Operate lays the canvas out at 1280, 1440, 1920 or 2560 pixels and scales it to fit, so the designer shows what the operator will see. Ctrl+Shift+F searches controls, pages, diagrams, nodes, tags and types at once, and a hit opens where it lives.
  • Types. A record the project knows by name - its fields with their kind, unit and range - kept in types and shown in the project pane. A Bundle or an Unbundle can follow a type instead of listing field names: add a field to the type and every node that follows it grows the terminal. Types travel with a module, and one already in the receiving project with the same fields is reused.
  • Modules. A page, a diagram (with the subdiagrams it calls) or the controls in hand, packed into one file and used in another project: Project › Export as a module…, and Import takes it back. On the way in it says what the module brings and which source of this project each of its tags should read, gives colliding ids new ones and rewrites every reference to them - so the second bench starts from the first.
  • Plan a bench that does not exist yet. The System view's Planned half: take an instrument the station knows, give it the id and address it will have, and it is on the bench as a plan with its own tags. Add to the station when it is wired; Simulate it there until it is, which puts the instrument's tags on the station as a simulated source with sensible ranges for their units, so a whole screen can be built, run and demonstrated before the hardware arrives. A station also serves one instrument in full at GET /api/templates/<id>.
  • A project pane and a System view. The left pane now holds the project - pages, diagrams, sources, with counts, and a line opens what it names. A third tab beside Panel and Diagram shows the system: the stations this project knows, each device wired to them with its state, address and live tags, and the screens each station serves. The tags there drag onto the panel and the diagram, and a station can be adopted as a source of the project in one click.
  • Measure a source before you build on it. Every source in the bar has a measure link: its tags with live values, units and the age of each sample, a write on any tag the source takes one for (the value that comes back is the source's, not the one you typed), a trace of the selected tag, and Add to the page. It is the first thing to do with a new instrument or PLC, where nothing else can be wrong yet.
  • A bench with no hardware. node studio/bench/instruments.js runs a Modbus TCP PLC, a SCPI power supply and an HTTP gateway on your own machine, all measuring one small simulated plant - a temperature that follows its setpoint with lag, a flow that follows the pump, a motor that draws current - and prints the source definitions to paste in. The station's drivers treat them as they treat a device: the same requests, the same scaling, the same writes. A whole screen, its diagrams, its alarms and its logs can now be built and proved before an instrument is ordered.
  • The designer and the running screen are one product. A screen a station serves carries Edit this screen, which opens the project behind it in the designer on that station (?open=<app>); the station's own page offers the same next to every app. The designer's toolbar shows which station and which version the project in hand belongs to, and remembers it across a reload; the Publish dialog lists what the station runs, with Open here for any of them and for any kept version; publishing when the station has moved on asks first, naming who published and when. A running screen notices within twenty seconds when a newer version is published or when the station is rolled back, and says so - a kiosk screen reloads itself.
  • What four QA agents found, fixed. Among them: the station's home page was open without a token and printed tag values and source addresses, credentials included (now behind the same token as the API, and every address shown without its password); a licence that was refused unlocked more than a valid one (a refused token now unlocks nothing); a damaged versions.json silently destroyed the version history (it is rebuilt from the versions on disk, and no kept version is overwritten); DELETE of an app that does not exist answered "removed" and wrote that into the audit trail; a diagram set to run on the agent computed null for ever in silence (a Panel node with no panel now reports it); two apps raising the same alarm id cleared each other's alarms; writing text to a numeric tag killed the tag while still reporting good quality; a station started wide open when --config named a file that was not there; --help, a port already in use, a broken configuration file and a misspelt configuration key each answer in a sentence now; --make-cert no longer overwrites a plant's own certificate; an identical republish no longer rolls a version off the history.
  • Accessibility, measured. npm run studio:a11y walks the designer in its three themes, a published app and the documentation against WCAG 2.2 level AA - names computed the way a browser computes them, contrast resolved against the first opaque background behind each text, focus reached with real Tab presses, targets measured as the area a pointer can hit, reflow at 320 px - and writes studio/a11y/report.json, from which the new accessibility page is generated, limits and all. Twelve of its twenty-two checks failed the first time and are fixed: the designer had no main region and no skip link, a component on the canvas had no name, every inspector field's label named nothing (no for), the Operate box and the palette group headers were under the 24-pixel target, the Delete button was unreadable on the dark theme, a Stop button was just under the contrast minimum, section headings skipped a level, and nothing honoured a reduced-motion setting.
  • Five QA rounds. Hostile input at the agent, the engines under odd values, the designer used every way, the agent under operations for two passes, and the docs against the code - what they found is fixed in this release: a message flow made in the designer could not be re-imported (its kind was misspelt), names from a project file reached the designer's markup, the formula parser could overflow the stack, a delay past 24.8 days fired at once, the hub kept a set for every tag name a client ever subscribed to, the first status call loaded node-opcua (900 ms, 100 MB) to say whether it is there, and a folder zip from any tool but the designer was refused. New: a page is renamed and removed from the page inspector; the designer reads deflated zips; limits.wsPingMs; logs.maxLineBytes; the scadastudio_subscriptions, scadastudio_subscribed_tags and scadastudio_process_external_bytes gauges.
  • The artefact tested. npm run studio:dist:check unpacks the release zip elsewhere, runs the agent from it and drives the designer, a publish, the app, the docs and the embed - the acceptance test the release procedure asked a person to do. It found the documentation's typeface refused by the agent's policy (Google Fonts is allowed now) and a stale answer overwriting the versions list.

1.17 (2026-09-22)

Adoption: what a team that keeps things in a repository, a portal that shows a screen, and a bench in another language ask for.

  • Versions on the agent. Every publish is kept (apps/<name>/versions/v<n>, twenty by default); the Publish dialog lists them with who, when, pages and flows, and Make current rolls back to any of them - the agent serves it again and restarts its flows. GET …/versions, POST …/rollback; the trail has both.
  • A project in a repository. Studio.serialize() writes a project the same way every time; Studio.migrate() brings older files to the current schema and Studio.validateProject() names what is wrong, field by field; the format is a JSON Schema (schema/project.schema.json). scripts/studio-project.js splits a project into a folder (a file per page and per flow), joins it back, checks, formats and publishes as a CI step; the designer exports and imports the same folder as a zip.
  • The designer in German, French, Spanish and Chinese (Project › Language, ?lang=): the chrome, the palette, the inspector, and the components' own words through the library's locale packs. A published app takes ?lang= for its components.
  • The embed. studio-embed.js from the agent and ScadaStudio.mount(element, { agent, app, page, token }), or <scada-studio-app>: a published app inside any page, its tags live. The guide.
  • The site. The download with its SHA-256, signature and public key, the editions and prices, the roadmap, the security and hardware pages linked from the landing page.

1.16 (2026-09-22)

Truth and integration: the agent inside the plant's architecture, the users from the company's directory, the licence that binds, and an honest page about hardware.

  • MQTT and HTTP sources. mqtt (MQTT 3.1.1 without a dependency: topics with wildcards, retained messages, JSON fields by path, publishes at QoS 0 or 1, TLS) and http (a JSON URL polled, fields by path, a write as a request from a body template) - the way a Python script, a gateway, a Node-RED flow or a web service feeds the panel, and the way a page writes back.
  • An OPC UA server on the agent (opcuaServer): every tag of the hub as a variable with its unit, quality and source timestamp, writable where the source is; a SCADA or a historian reads the station like a PLC. The OPC UA client speaks Basic256Sha256 with Sign or SignAndEncrypt, its certificate made in the pki folder next to the configuration, the server's accepted on first sight or from the trusted folder.
  • Modbus: coils and discrete inputs (functions 1, 2, 5, 15), block reads - a poll reads every tag of a unit and a kind within a gap in one request - and a unit id per tag.
  • Users from the identity provider: auth.oidc - OpenID Connect with PKCE against Keycloak, Entra ID, Okta, Auth0 or ADFS, the id_token verified against the provider's keys, groups mapped to roles, Sign in with your organisation on the app's sign-in dialog. An Enterprise feature.
  • The licence binds. Editions - developer seat, station, Enterprise, Community, trial - perpetual with a maintenance date (a build newer than it says so), a station token bound to the station's fingerprint from its offline activation request (node agent.js --activation-request), a revocation list, and the features of the Enterprise edition (OpenID Connect, the OPC UA server, the audit export) named and not started under a token that lacks them. scripts/studio-licence.js sign | activation | verify | revoke.
  • Instruments from a template. Templates for a Keysight 34461A, a Rigol DM3058 and DP800, a Siglent SDG1000X, a Keithley 2400 and a Tektronix TBS1000, added to an agent from the designer's Sources dialog (pick, address, Add to the agent) or POST /api/sources; DELETE removes; the configuration file follows.
  • Verified hardware. hardware.json says, path by path, what has met a device, what only the emulator, what neither; scripts/check-hardware.js runs a driver against a device on a bench and records the result; the hardware page is generated from it and the Sources dialog shows the state next to each template. Nothing is claimed beyond it.
  • The SCPI driver's writable tags take a values map ({ "true": "ON", "false": "OFF" }); /api/status carries each source's stats; the check suite reads the TDMS log with npTDMS when Python has it. agent:check 76.

1.15 (2026-09-21)

The trust gate: what a station's owner and a buyer's security review ask of a server on their network, built and checked.

  • The formula language. A Formula node and a Math node are no longer JavaScript evaluated by the page: their text is read by the product's own interpreter (studio-formula.js), which reaches the node's inputs and the functions of Math and nothing else, in the browser and on the station alike. ^ is a power. The reference.
  • Code is a policy. A Function node is code, and a station runs it only with "flows": { "allowCode": true }; without it a project with Function nodes is refused at publishing, with the nodes named. With it, agent-side Function nodes run in a sandbox thread per app with a time limit.
  • The fence around the port. Browser origins allowed by list (origins), the agent's own and the same machine by default; tokens only in the Authorization header and in the WebSocket's first message, never in a URL; request bodies capped, requests and logins per address limited, WebSocket frames capped at 1 MiB (closed with 1009), clients and subscriptions limited, silent clients dropped; a Content-Security-Policy on every page with its inline scripts by hash, nosniff and no-referrer; every value from a project escaped on the home page.
  • Secrets stored as hashes; sessions that survive a restart. --add-token prints a token once and stores its SHA-256; sessions live in sessions.json under an HMAC (hashes only), and an edited file is refused whole.
  • The audit trail. audit.jsonl, hash-chained: start and stop, logins, writes with the value before and after, publishes with the project's hash, removals, users and tokens added, the licence; /api/audit (JSON or CSV) and /api/audit/verify; --audit-verify from the command line.
  • Running as a service. --install makes a Windows service through WinSW when it is present, else a scheduled task from XML that never times out and restarts on failure; /healthz and /metrics (Prometheus text). CSV logs rotate at logs.maxBytes and are written off the event loop, in order. Inject nodes can repeat at an interval.
  • Documents. The security page rewritten around what is now true - the fence, the code policy, the trail, how the product is developed (IEC 62443-4-1 practices), the disclosure policy and the response times, the Cyber Resilience Act reporting, supported versions - with SECURITY.md and a CycloneDX SBOM (sbom.cdx.json) in the download, which now also carries its SHA-256 and a signature.
  • The library's WebSocket adapter takes an openMessage, sent on every open before the subscriptions replay; the runtime uses it for the token.

1.14 (2026-09-21)

  • The panel and the diagram are one thing. A control placed on the panel gets its terminal on the diagram at once (a Panel value, indicator or control node, named by the owned label, in the left or right column of the diagram shown); double-clicking a control finds its terminal, double-clicking a terminal finds its control (on its page); the inspector's Find terminal / Create terminal; a deleted control takes its terminals with it; the Panel side has the run arrow for the app's diagrams. Terminals on the diagram are named by the control's owned label ("Frequency (Hz)" rather than "freq.value").

1.13 (2026-09-21)

  • The grey front-panel theme is the Bench theme ("theme": "bench" in a project file). The product describes itself in its own words throughout: the classic controls palette, the classic first program, the graphical way of writing a program.

1.12 (2026-09-21)

  • Enum constant (one of a list of names, chosen in the inspector; the state machine pattern uses it) and Note (the classic free label) on the palette.
  • An action of the right-click menu - a control created for a terminal, a diagram from a pattern - is one undo step.
  • A new guide, Coming from graphical dataflow: the usual terms mapped to Scada Studio's, what is the same, what is different, what is not there.

1.11 (2026-09-21)

  • Output tunnels on the Case frame (the output tunnels field): each case wires its own value to the tunnel on the frame's right edge and the diagram reads it outside - the classic output tunnel, which makes the state machine pattern possible; a case that wires nothing is a warning.
  • Patterns in the New diagram dialog: Acquire, analyse, display; State machine; Event handler; Measurement loop - laid out and running.
  • Align and distribute tools for several selected nodes; a wire's tooltip (where it goes, its type, what passed last); a diagram's icon (up to three characters) on the Subdiagram nodes that call it. Fixed: since 1.9 the editor refused a wire from inside a While loop to its stop terminal (the check meant the loop's own inputs).

1.10 (2026-09-21)

  • The right-click menu, the classic shortcut menus: on a terminal, Create constant, Create control on the panel and Create indicator on the panel (a control of the type from the controls palette, placed and wired); on a wire, Insert a node into it; on a node, Replace with, Duplicate, Copy, the breakpoint, Help, Delete; on a Case or Sequence, Show, Add a case after, Remove this case; on the canvas, Paste, Select all, Clean up, the Error list, the context help.
  • The clipboard: Ctrl+C copies the selection with its wires - as text, so it pastes into another diagram or another project - and Ctrl+V pastes it. Redo with Ctrl+Y.

1.9 (2026-09-21)

  • The broken arrow and the Error list: the diagram is checked whenever it is drawn - wire loops, wires to terminals that are gone or of the wrong type, a frame's selector or count wired from inside, nodes that name nothing, a Formula that does not compile - and an error breaks the run arrow; the arrow opens the list, a line of it selects what it is about, a broken wire is drawn red and dashed; warnings are listed and the diagram runs.
  • Formula node with named terminals, as a formula node should have: inputs and outputs name them; one line is an expression, several lines assign the outputs.
  • Find (Ctrl+F) on the diagram. The Waveform Chart draws its y and time scales (the strip chart's new showScales), and clips its trace to the plot.
  • After a second QA pass: a Feedback node inside a Case, Event or Sequence within a loop latches (it gave its initial value every iteration); a node moved into another case loses the wires that would cross cases, and the check reports such a wire; the check knows an optional component (Alarm), a wired Event timeout, the agent's own agent.flows.* tags, a Feedback in a frame of a loop, a Case whose cases do not cover a boolean selector, a self-wire, a broken or circular Subdiagram callee; Formula terminal names must be JavaScript names and the body runs strict (an undeclared name is an error, reported once); the Error list opens from the inspector too (warnings), a line about a wire selects the wire, the lines take the keyboard; the last run's errors belong to the diagram that ran; Escape in Find or Quick Drop no longer deselects. On the panel: a slide takes its cell's height with or without a label and a vertical one keeps its width at the left, a numeric or a string is one line high under its label, one-row buttons fit, controls have a smallest size (a knob's dial, a slide's scale, a chart's plot), captions carry a tooltip, the Table preset is six rows, the andon board's stack lights are three rows, the signal generator's RMS history runs on a time axis.

1.8 (2026-09-21)

  • Sequence frame (the flat sequence structure), Decorations (Raised Box, Recessed Frame, Flat Frame, behind the controls), and icon terminals: a front-panel terminal on the diagram shows its control's picture.
  • After a QA pass over the panel, the diagram engine and the site: NaN and Infinity travel down the wires as IEEE numbers do; a Subdiagram call releases what the callee held; a run stopped during a loop's wait or a Wait settles at once; a While loop's i after its stop terminal is the last iteration; a finished diagram returns the run arrow; Clean up lays out the cases not shown; a copied frame lands beside the original with its contents; removing a Case keeps the case shown; a frame's selector, count and period cannot be wired from inside; a For count that is not a number runs nothing; a control fills its cell under an owned label; Ctrl+A on the panel needs no selection; the Table preset has columns; the site copy carries the agent's documentation.

1.7 (2026-09-21)

  • The front panel, the classic way. The palette opens with the classic groups - Numeric, Boolean, String, Graph, List & Table - each tile a preset of an element with its properties, size and owned label; every component may carry an owned label. A Bench theme draws the panel as graphical dataflow tools do. The studio's own LED element. The For loop auto-indexes its collect terminal into an array. The first project is the signal generator, the classic first program. Breakpoints (Alt+click) pause the diagram before a node, with Step and Continue; Quick Drop (Ctrl+Space) places a node by name.

1.6 (2026-09-21)

  • The front panel, the classic way. The controls palette shows a picture of every component. Several components select by shift-click or by a marquee drawn on empty canvas, move together, and align with the inspector's alignment tools (edges, centres, distribute, same size). An Event frame in the diagrams waits for a front-panel event and runs its body with the event's value - the While-loop-around-an-Event-structure pattern. On the diagram: several nodes select by shift-click, marquee or Ctrl+A and move, duplicate (wires kept) and delete together; Clean up (Ctrl+U) lays the diagram out by its wires; a selected wire is a probe. On the panel, a list of records (pens, alarms, states) is edited as a table. the classic keys: Ctrl+E, Ctrl+R, Ctrl+., and Ctrl+H for the context help.

1.5 (2026-09-21)

  • The classic look. The two sides are Panel and Diagram. The diagram is drawn as a block diagram: a white dotted canvas, icon nodes tinted by family with typed terminals on their edges, constants as value boxes, tags and front-panel objects as terminal boxes with the type's abbreviation (thick for controls, thin for indicators), loops with i, N and the stop terminal on their thick border, the Case selector on the frame's edge, right-angled wires coloured by type (dashed booleans, dotted text, thick arrays), a run arrow that breaks on errors and an Abort square, execution highlighting from the lightbulb, an icon palette with a Front panel group of the page's own terminals. The panel runs its diagrams while it is shown, as the app does. The first project is the bench meter, whose statistics are now a block diagram; ?project= opens any project file in the designer.
  • On the industrial site. The designer, the app page, the samples, the runtime and this documentation are published under the site's studio/ folder, with a landing page (Studio in the header) and a section on the home page.

1.4 (2026-09-21)

  • Case, subdiagrams and records in the diagrams: a Case frame runs the case its selector names and shows one case at a time; a diagram with Diagram input and Diagram output nodes is called by a Subdiagram node, whose terminals follow the callee's; Bundle and Unbundle make and take apart records. Terminals that follow a node's fields, in the engine and the editor. The sample's agitator watch uses a Case and calls Percent of band.

1.3 (2026-09-20)

  • Dataflow diagrams (level C): a second kind of flow with typed terminals, one value per wire, loops as frames and feedback between iterations - 60 node types in studio-dataflow.js, the same engine in the browser and on the agent. The flow editor serves both kinds: typed wires refused with a reason, frames that carry their body, every node's last output on the canvas. The plant overview gains a diagram; the guide and the diagram node reference are new.

1.2 (2026-09-20)

  • Users with passwords on the agent: --add-user stores an scrypt hash; POST /api/login opens a session that travels like a token; five failures block an address; an app whose agent asks for a login shows a sign-in dialog and keeps the session in its tab; the designer's Publish dialog signs in too.
  • TLS on the agent: --make-cert writes a self-signed certificate for the hosts given (an EC key and an X.509 certificate written by the agent itself, no OpenSSL); tls in the configuration makes it speak HTTPS and WSS; a plant's own certificate goes in the same fields.

1.1 (2026-09-20)

  • OPC UA on the agent (opcua source, through the node-opcua package): the tags as monitored items of one subscription, writes through the session with the variable's own type, user-name or anonymous sessions, the security policies node-opcua offers, reconnection, and /api/sources/<id>/browse to find node ids. Checked against an OPC UA server the check script runs.

1.0 (2026-09-20)

The first release: the designer, the runtime, the flows and the agent as documented here, with the licence mechanism, this documentation, and a distribution built by npm run studio:dist.

  • Designer: pages of Smart.Industrial components on a grid, an inspector generated from the components' metadata, bindings by drag, feeds for charts, write-back per event, pages from templates, sources in a dialog, undo, copy and paste, export and import, preview, publish; a first-run welcome; the Licence dialog.
  • Flows: 26 node types, one message shape, run in the browser with the page or on the agent around the clock; the editor with wires by drag, a message strip, CSV download; the node reference generated from the engine.
  • Agent: Modbus TCP and RTU, SCPI over TCP and serial, VISA (socket, VXI-11, serial in the agent; USB, GPIB and HiSLIP through the library), DAQmx through its library, a simulator; one WebSocket; the published apps; the designer; flows with alarms, published tags, CSV and TDMS logs; tokens and roles; running at boot.
  • Apps: a health strip when a source is down, kiosk mode, the evaluation line.

Milestones on the way

  • M0 - the project format, the runtime on Smart.Industrial Connect, the designer, the first sample.
  • M1 - the source dialog, page templates, five sample projects, relative times, copy and paste, the grid dialog.
  • M2 - publishing to an agent or as a zip; the agent with Modbus TCP, SCPI and the simulator over a WebSocket.
  • M3 - level B: the flow engine and editor, flows on the agent.
  • M4 - agent v1: serial lines, VISA and VXI-11, DAQmx, TDMS, tokens and roles, running at boot.
  • M5 - licensing, documentation, the app's health strip, the distribution.

The full log with every change is CHANGELOG.md in the repository.