[server] listen = ":8080" storage_dir = "./interfaces" # ── UI presentation defaults (sent to the browser) ────────────────────────── # default_zoom is the base UI scale used when a browser has no saved zoom of its # own. Handy for HiDPI screens (e.g. 4K) whose OS scaling is left at 100%, where # the browser reports devicePixelRatio=1 and the UI would otherwise be tiny. The # in-app A+/A− control still overrides it per browser. 1.0 = no scaling. [ui] default_zoom = 1.0 # e.g. 1.5 or 1.75 on dense displays # ── Identity & access control ─────────────────────────────────────────────── # The end-user identity is read from a header set by a trusted authenticating # reverse proxy. Leave trusted_user_header empty for unproxied/dev/LAN use. # SECURITY: only enable this when the proxy strips any client-supplied value of # this header, otherwise it can be spoofed. trusted_user_header = "" # e.g. "X-Forwarded-User" # Identity used when the trusted header is absent/empty. Empty = anonymous. default_user = "" # ── Native Kerberos / SPNEGO authentication ───────────────────────────────── # Alternative to a reverse proxy: uopi authenticates browsers directly via # SPNEGO ("Negotiate"). Recommended when proxy-header auth lets some browsers # (notably Firefox) silently fall through to default_user. When enabled, uopi # challenges API requests with 401 WWW-Authenticate: Negotiate and resolves the # user from the validated Kerberos ticket (short principal name, realm stripped); # any inbound trusted_user_header value is ignored to prevent spoofing. # Browsers must be told to perform SPNEGO for this origin # (Firefox: network.negotiate-auth.trusted-uris). [server.kerberos] enabled = false # Path to the service keytab holding the HTTP service principal's key # (e.g. HTTP/host.example.com@REALM). Required when enabled. keytab = "" # e.g. "/etc/uopi/http.keytab" # Optionally pin which keytab principal to accept tickets for. Empty = default. service_principal = "" # e.g. "HTTP/host.example.com" # ── Built-in HTTP Basic authentication (PAM) ──────────────────────────────── # Another standalone alternative (no reverse proxy, no Kerberos): uopi challenges # with 401 WWW-Authenticate: Basic and validates the username/password through the # host PAM stack (/etc/pam.d/). On an SSSD/LDAP-joined host this # reuses each user's normal login password with no directory config in uopi. The # validated username feeds the same access pipeline. Mutually exclusive with # Kerberos. REQUIRES a PAM-enabled build: `make backend-pam` (cgo + libpam); the # default static binary cannot validate and refuses to start with this enabled. # Basic credentials travel on every request — enable [server.tls] below unless on # a fully isolated network. [server.basic_auth] enabled = false # PAM service name under /etc/pam.d/. Empty defaults to "uopi". pam_service = "" # e.g. "uopi" or "login" # ── Built-in HTTP Basic authentication (LDAP) ─────────────────────────────── # Pure-Go alternative to basic_auth/PAM: validates the Basic credentials directly # against an LDAP directory using "search then bind" — the same flow an SSSD/LDAP # client uses. Needs NO cgo, so it works in the default fully-static binary # (`make release`), unlike PAM. Mutually exclusive with kerberos and basic_auth. # Defaults mirror SSSD: user_attr → "uid", user_object_class → "posixAccount", # empty bind_dn → anonymous search. Enable TLS below (or use ldaps://) so # passwords are not sent in clear text. [server.ldap] enabled = false # Directory endpoint(s), tried in order (SSSD ldap_uri). uri = ["ldaps://ldap.example.com"] # Subtree user entries live under (SSSD ldap_search_base). search_base = "dc=example,dc=com" # Attribute matched against the login name. Empty defaults to "uid". user_attr = "" # e.g. "uid" (RFC2307) or "sAMAccountName" (AD) # Object class to restrict the search. Empty defaults to "posixAccount". user_object_class = "" # e.g. "posixAccount" or "user" (AD) # Optional service account for the search. Empty bind_dn = anonymous search. bind_dn = "" bind_password = "" # Upgrade a plain ldap:// connection to TLS before binding (ignored for ldaps://). start_tls = false # Optional PEM CA bundle to trust a private directory CA. ca_cert = "" # e.g. "/etc/uopi/ldap-ca.pem" # Disable TLS certificate verification (testing only — insecure). insecure_skip_verify = false # ── Built-in TLS / HTTPS ──────────────────────────────────────────────────── # Terminate HTTPS directly without a reverse proxy. Strongly recommended whenever # basic_auth is enabled. Cert and key are PEM paths; both required when enabled. [server.tls] enabled = false cert = "" # e.g. "/etc/uopi/tls/cert.pem" key = "" # e.g. "/etc/uopi/tls/key.pem" # Role-based access through group memberships. Each [[groups]] block lists # members by role; a user's effective global capability is the highest role # across all their memberships, along this ladder: # viewer < operator < logiceditor < auditor < admin # Capabilities: operator+ may write signals; logiceditor+ may edit panel and # control logic; auditor+ may view the audit log; admin may open the admin pane. # # The built-in "public" group is implicit (every user is a viewer of it). A # config with NO roles assigned anywhere is fully open (everyone is admin), for # trusted-LAN/dev use; assigning any role switches to strict mode where unlisted # users are read-only viewers. Once changed at runtime via the admin pane, # {storage_dir}/access.json supersedes the groups below. # # Groups may nest via "parent": a member of a parent group inherits its role on # every descendant group too (unless overridden lower). # [[groups]] # name = "public" # admins = ["alice"] # alice is a global admin # [[groups]] # name = "operations" # operators = ["bob"] # auditors = ["carol"] # [[groups]] # name = "engineers" # parent = "operations" # bob inherits operator here # logiceditors = ["dave"] # viewers = ["erin"] [datasource.epics] enabled = true ca_addr_list = "" # overrides EPICS_CA_ADDR_LIST if set archive_url = "" # EPICS Archive Appliance base URL channel_finder_url = "" # e.g. http://channelfinder:8080/ChannelFinder auto_sync_filter = "" # e.g. area=StorageRing or tags=production [datasource.synthetic] enabled = true