Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
opcda-bridge-gateway
Windows gateway that exposes native OPC DA (COM/DCOM) servers over the network to
opcda-bridge-client and other gRPC clients.
The supported Windows gateway target is 32-bit x86
(i686-pc-windows-msvc), including on 64-bit Windows. This is deliberate: the gateway loads
native OPC DA/COM components alongside the server, and legacy installations may expose those
components only through the 32-bit COM and registry view. Windows runs this gateway through
WOW64 on a 64-bit host. The x86 gateway is the supported Windows gateway; a host-default x64
build is not a substitute.
Install it with Cargo on a Windows host:
rustup target add i686-pc-windows-msvc
cargo install --locked --target i686-pc-windows-msvc opcda-bridge-gateway
The gateway is versioned independently from the client and protocol crates. Prebuilt archives use
opcda-bridge-gateway-vX.Y.Z tags; client/gateway interoperability is determined by the wire
protocol and advertised capabilities rather than matching package versions.
The gateway-wide GetGatewayInfo RPC advertises the protocol-feature ranges supported by the
running binary without opening an OPC DA server. The client compatibility command uses this
handshake for deployment checks; GetCapabilities remains the per-server operational capability
endpoint and supports older clients.
The gateway must run on the Windows machine hosting the OPC DA server. See the repository README for service installation, configuration, and firewall setup.
Tag browsing uses native one-level OPC DA enumeration with bounded pages. The gateway owns opaque
browse sessions and continuation tokens, preserves exact ItemIDs separately from display names,
and reports whether a page is complete. Only selectable item and branch-and-item nodes expose
ItemIDs; branch-only nodes retain their private navigation identifiers behind opaque node keys.
Namespace search is a bounded progressive operation that can be cancelled by dropping the client
stream.
DA3 root ItemIDs and unused filters are sent as required non-null empty strings. A server that
also supports DA2 falls back only when its first DA3 root browse returns
RPC_X_NULL_REF_POINTER or E_NOTIMPL, and reports that compatibility decision explicitly.
The gateway also exposes persistent indexed-search status, refresh, pause, resume, cancel, delete,
and query operations. A first manual refresh validates the exact ProgID against current server
discovery, persists enrollment, and begins indexing immediately; an unknown ProgID is rejected
without durable state. Capability responses advertise indexed-search support, its protocol version,
the configured result limit, and the server's index state.
Indexed results contain exact ItemIDs and breadcrumb labels, never browse-session node keys.
Refreshes run asynchronously, and gateway shutdown cancels active indexing before the process exits.
After an inventory reaches a terminal event, the gateway settles and drops the native inventory
stream before it records the generation outcome or releases build ownership and the file lock.
This keeps a subsequent build from starting while the previous native worker is still joining.
Foreground operations are reference-counted per server; indexing stays paused while any foreground
user is active and remains paused through the configured quiet period after the last foreground
operation ends.
An inventory can complete successfully with a non-fatal warning when the OPC server rejects
specific namespace branches; the generation remains active and usable, and the status diagnostic
is reported as a warning unless the index state is failed.
Each build checks its maintenance-window, health, and adaptive recovery gates before requesting
the next inventory event. Health readiness combines capability and latency checks with the
optional sentinel tag; unhealthy targets pause inventory with bounded exponential backoff and
healthy recovery resumes it with the configured pacing. Without a sentinel tag, the health
status is reported as Unavailable while capability and latency checks can still permit the
build. Cancellation, health failure, or a rejected pacing update terminates the build without
replacing the last complete generation.
Pending entries are flushed before terminal state is recorded, and successful completion with a
non-fatal inventory warning remains searchable while the warning is exposed in status.
Each terminal build emits one structured namespace index build telemetry log record. It includes
the terminal outcome and timing, the last progress snapshot (last_progress_entries_seen and
last_progress_unique_items), the authoritative persisted row count (persisted_items), active
and paused time, inventory slice counts and elapsed-time totals, native operation totals, DA2/DA3
slice counts, entry-kind counts, SQLite commit counts and latency summaries, and any failure text.
It also records drained_events, received_entry_events, pending_entries, and
pending_unique_items. The first two count events consumed by the gateway and entry events
received from the native stream; the latter two describe entries still buffered when terminal
cleanup was reached. For an interrupted build, these fields make a difference between progress
and persisted rows measurable rather than inferred from partial counts alone.
The progress snapshot can differ from the persisted count because progress and terminal inventory
events are asynchronous; use the promoted generation's persisted counts for acceptance and
throughput calculations. Successful generation promotion emits a separate log record with its
duration. These records are diagnostic only and do not change the index schema or runtime pacing.
Completed active generations are durable across gateway restarts. Activation is an atomic metadata
transition, and promotion status uses a read-only SQLite connection plus filesystem diagnostics, so
status remains responsive even while the writer is in the promotion critical section.
Only one build for a server can hold its gateway-wide file lock at a time; contention reports the
owning process metadata. On Windows, that metadata is kept in an adjacent .build.owner sidecar
because the locked file itself may be unreadable. The owner sidecar is removed on a clean lock
release and can remain after forced termination until the next acquisition overwrites it; the
operating-system advisory lock, not the sidecar's existence, determines whether a build is active.
Superseded and abandoned data is reclaimed in bounded background batches through a separate
SQLite WAL connection, coordinated by a database-wide writer gate shared with every build mutation
for the same database file, including builds for other servers. Cleanup defers while any build is
active, keeps its request registered while waiting, yields between batches so a build can make
progress, and resumes pending requests after the last build finishes even when the request came
from another manager instance sharing the database. Shutdown is also observed when it races with
that deferred wait. An interrupted refresh is superseded when a complete active generation
remains available, so status and search continue to use that snapshot while cleanup runs.
An interrupted initial build remains failed and visible because no complete snapshot can replace it.
If SQLite recovery quarantines an invalid index, the database and its -wal/-shm sidecars are
kept together so committed data remains available for diagnosis.
Status combines the persisted generation snapshot with runtime build, health, storage,
foreground, and scheduler diagnostics. During promotion, persisted status is read through a
read-only connection; a runtime error overrides the reported state only when no build is active.
Database coordination and persistent build-lock paths use the canonical identity of the database
file, so existing-file aliases such as relative paths and symlinks cannot bypass coordination.
If the file and its parent cannot be canonicalized, the original path spelling is retained.
Independent in-memory databases are isolated from the registry and do not create filesystem
build-lock sidecars.
Uncached indexed searches use a separate read-only SQLite connection and rank only bounded
candidate sets in memory, so a broad query cannot hold the coordinator's foreground database
mutex while it scans the FTS index. Exact searches use separate covering equality lookups on the
normalized display-name and ItemID indexes, each bounded to limit + 1 rows, exclude lower-priority
ItemID duplicates already found by the display-name probe, and then merge and deduplicate those
candidates before ranking. Prefix searches use indexed lexicographic ranges rather than
generation-wide LIKE scans; contains searches retain their FTS path. Status, discovery, reads,
writes, and lazy browse therefore remain available while search work is in progress. Matching is
case-insensitive with exact/prefix/contains ranking, and
responses report when additional results exist beyond the requested limit. During promotion,
searches use the active generation already returned by the promotion-safe status path instead of
waiting for the writable database mutex.
Refresh setup is staged before the asynchronous build task is launched. If startup, capability
negotiation, generation creation, task launch, or shutdown fails at that boundary, the
provisional generation is abandoned and its build reservation is released while the last
complete active generation remains available.
Cancellation
requests received before inventory startup returns its control handle are retained and applied
once the handle is available.
Read responses contain semantic values. For an OPC DA VT_BSTR, the gateway forwards the exact
BSTR contents without adding display quote characters; quotes remain only when present in the
server value.
Configure index-wide behavior in the gateway TOML file under [index]. SQLite owns enrolled
servers and each server's auto-refresh setting; a fresh gateway has no enrolled servers and never
starts an automatic first build. A successful manually enrolled index is refreshed weekly by
default when its per-server auto-refresh setting and global index.enabled switch permit it.
Disabling per-server auto-refresh preserves its searchable generation; deleting an index removes
its enrollment, generations, entries, and retry state after coordinating any active build. Delete
returns a temporary deleting status while cleanup runs, then reaches not-indexed. The gateway
uses a service-writable SQLite database, conservative batch/rate/duty-cycle defaults, a
two-second foreground quiet period, and one build at a time.
Native inventory batches are bounded to 1,000 entries by the OPC DA client contract.
Native inventory slicing and SQLite commit batching are independently bounded: the default
SQLite commit batch is 1,024 entries, so a commit may span multiple native slices. Adaptive
controller decisions update the native slice batch size and item-rate limiter; the native
minimum operation interval remains a separate control and is zero for gateway-generated
inventory pacing. The item-rate limiter is charged by the native operation's item cost and must
not be converted into a batch-size-derived sleep. The commit interval provides a time limit for
low-volume inventories, flushing pending entries even when the batch threshold has not been
reached.
index.worker_count controls independent namespace workers within one build
(default 1, maximum 4). When it is greater than one, the gateway uses
hierarchical root browsing to partition safe canonical branches across fresh
root-scoped inventory streams. Root-level items are emitted directly and
duplicate ItemIDs are suppressed. If the server cannot provide a complete,
session-backed hierarchical root page with at least two expandable branches,
the gateway falls back to one full-root inventory.
Set index.inventory_root to an exact canonical ItemID to inventory only that
subtree; this uses the client's root-scoped API directly and takes precedence
over automatic root partitioning.
Coordinated workers share pause, resume, pacing, and cancellation controls;
all worker streams are shut down and joined before the build releases its
ownership.
Runtime status includes rolling
foreground latency/error/quality metrics, host/storage availability, and persisted scheduler
backoff diagnostics.
If the native client rejects an initial or adaptive pacing update, the build fails visibly and
the previous complete generation remains active; pacing errors are never logged and ignored.
Completed enrolled generations are refreshed weekly by default. Maintenance windows, startup grace,
and deterministic per-server schedule jitter prevent scheduled work from starting at unsuitable
times or in lockstep across targets.
The default database path is %PROGRAMDATA%\\opcda-bridge\\index.sqlite3 on Windows and
$XDG_DATA_HOME/opcda-bridge/index.sqlite3 (falling back to
$HOME/.local/share/opcda-bridge/index.sqlite3) on Linux/macOS. See the example file for all
available settings, including maintenance windows, health thresholds, and adaptive AIMD
rate/batch/duty-cycle floors and ceilings. Adaptive indexing starts at the canary profile and
backs off or pauses when recent foreground OPC errors or bad-quality reads, or host/storage
guardrails, deteriorate.
Pre-build and health OPC operations are bounded by operation_timeout_seconds, so an
unresponsive target cannot hold the scheduler indefinitely.
An optional sentinel_tag is read during health probes; omitted or unavailable sentinel
configuration is reported explicitly rather than treated as a healthy zero value. Status also
distinguishes a configured sentinel from its probe result, so an unprobed sentinel is not reported
as absent.
The gateway migrates the previous indexed-search database schema in place through the schema 2 -> 3 -> 4 chain. Existing generations, relational entries, and full-text search data are preserved; servers with a usable active generation are enrolled for automatic refresh, while failed-only histories remain visible and require a manual retry before scheduled refresh is enabled. Each migration step is transactional, so a failed upgrade is reported without leaving a partially upgraded index database.