portal.v1
The API the Portal calls. Source: platform/nexus/proto/portal/v1/. Every
service is served by nexus over Connect; all but ReleasePublisherService
authenticate a user with a Zitadel access token.
portalAuth (the JWT interceptor) only establishes who is calling. Every
handler decides on its own whether the caller may proceed; the Gate column
below names that check. See Nexus → Authorization
and Authz → Model for what each gate means.
| File |
Service |
Mounted |
enrollment.proto |
PortalSessionService |
yes |
org.proto |
PortalOrgService |
yes |
gateway.proto |
PortalGatewayService |
yes |
admin.proto |
PortalAdminService |
yes |
users.proto |
PortalUsersService |
yes |
release.proto |
ReleasePublisherService |
yes, behind its own key |
link.proto |
PortalLinkService |
no — see below |
common.proto |
shared messages (Org, Permission, Role, …) |
— |
Gate shorthand used in the tables:
| Gate |
Meaning |
| device action |
requireDeviceAccess: the action on the device's org (portal:org), else on the device itself (portal:device), else platform admin. A holding-zone device (no org) is platform-admin only. Refusals are NotFound. |
| platform admin |
the * action on portal:system (PLATFORM_ADMIN) |
| device manage |
platform admin, or device:manage on portal:system (SUPPORT_ADMIN) |
| config read / write / apply / confirm |
device read/write, or a portal:segment grant on one of the device's managed instances — see segment scoping |
| self |
acts only on the caller's own subject; no other user's data is reachable |
PortalSessionService
| RPC |
Gate |
Purpose |
Me |
self |
Caller identity, derived roles (member, plus admin for PLATFORM_ADMIN) and raw permissions. Provisions nothing: a caller with no grant gets an empty permission list and the portal shows its no-organisation screen. Records the visit in portal_sessions. |
Enroll |
self |
Claim a device with an enrollment code. The code is the credential. |
Transfer |
self |
Mint a transfer code for a device the caller owns; ownership is enforced downstream. |
GetUserPreferences |
self |
Every preference of the caller, in one call. |
SetUserPreference |
self |
Write one preference; an empty value deletes it. |
PortalOrgService
| RPC |
Gate |
Purpose |
ListOrganizations |
filtered |
Orgs the caller owns (organizations.owner_id) plus orgs they hold a portal:org grant on. A platform admin also gets every other org, labelled admin. |
CreateOrganization |
platform admin |
Create an empty org owned by system. The admin is not made ORG_OWNER; the first owner is invited from the Team page. |
RenameOrganization |
org write, or platform admin |
Rename. The slug does not change. |
ListMembers |
org read, or platform admin |
Holders of a membership role (ORG_OWNER, ORG_MEMBER) on the org. Support grants are not listed. |
ListPermissions |
self, or platform admin |
ACL tuples of one subject. |
ListRoles |
none |
The static role catalogue. |
AssignPermission / RevokePermission |
grant authority |
Write or remove one ACL tuple. See who may grant. |
Resolve |
none |
Turn ids found inside records (config author, event actor, org) into display names. Never returns an email. |
ListOrgAccess |
org read, or platform admin |
Who has access to an org, including grants on its individual devices. Platform admins see every holder; everyone else sees membership roles only. |
ListPlatformAccess |
platform admin |
All portal:system grants, so they can be revoked from the portal. |
ListPortalSessions |
org audit |
Who used the portal for this org, and when. |
ListTemplateOverrides |
org read, or platform admin |
Per-org overrides on the service-profile catalogue. |
SetTemplateOverride |
platform admin |
Write one override. |
PortalGatewayService
Fleet and identity
| RPC |
Gate |
Purpose |
ListGateways |
filtered |
Devices in the orgs the caller can see. Each DeviceSummary carries caller_actions, what the caller may do on it. |
GetGateway |
device read |
One device: summary, presentable config, last state (state_data bytes and typed state), published_suffix. |
UpdateGateway |
device write |
Display name, hostname, model. Empty fields are left unchanged. |
GetGatewayState |
config read |
Last state report, filtered to the caller's segments for a partner. |
GetBlockTrend |
device read |
Blocked traffic over time, as reload-corrected increments. |
DiscoverEquipment |
device read |
Vendor names for the device's ARP table, from the IEEE MA-L listing nexus embeds. |
Lifecycle
| RPC |
Gate |
Purpose |
AssignGateway |
device manage |
Give a holding-zone device to an org, or move a device between orgs. Sets org_id, status='assigned'. |
UnassignGateway |
platform admin |
Clear org_id, status='holding'. |
ClaimGateway |
platform admin |
Fold a holding-zone unit into an existing device: its token and hardware move onto the target and the placeholder is retired. |
CreateDevice |
platform admin |
Stage a prepared device with no hardware. |
CloneDevice |
platform admin |
Stage a prepared device seeded with a copy of another's config as a draft. |
Reenroll |
platform admin |
Move a connected gateway to another environment: pushes a reenroll command carrying the target URLs and a token minted there. |
See Device Lifecycle.
Configuration
| RPC |
Gate |
Purpose |
GetGatewayConfig |
config read |
Latest presentable config, filtered for a partner. |
SaveGatewayConfig |
config write |
Store the submitted document as a new draft version. A partner's save is refused if it differs from the stored config outside their segments. |
ApplyGatewayConfig |
config apply |
Push a version to the device. Re-checks the partner scope against what the version would change now. |
ConfirmGatewayConfig |
config confirm |
Commit a version inside the confirm window (sends the confirm_config command). |
ListConfigVersions / GetConfigVersion |
device read |
History. The author is redacted for a caller without audit. |
RollbackConfig |
device write |
Re-apply an earlier version as a new version. |
The apply/confirm pair is the auto-rollback mechanism — see
Config Lifecycle.
Partner segments
| RPC |
Gate |
Purpose |
GrantSegmentAccess |
platform admin |
Grant a subject PARTNER on one managed instance; the instance must exist on the named device. |
RevokeSegmentAccess |
platform admin |
Withdraw it. Revoking an absent grant succeeds. |
ListSegmentAccess |
device read |
Who holds segment grants on this device. |
Observability and audit
| RPC |
Gate |
Purpose |
ListDeviceEvents |
device read + audit |
Identity and lifecycle changes that are not config versions (hostname, display name, model, org, hardware replacement). |
GetGatewayLogs |
device read + audit |
Query gateway_logs. |
GetGatewayMetrics |
device read |
CPU, memory, temperature series. |
Operations
| RPC |
Gate |
Purpose |
RestartAgent |
device device:manage |
restart_agent command. |
RebootDevice |
device device:manage |
reboot command. |
RunDiagnostic |
device device:manage |
ping / traceroute. Returns a command_id; the output arrives over the viewer WebSocket as a command_result with that id. |
GetAgentUpdateStatus |
device read |
Desired vs reported agent version and the current update phase. |
UpdateGatewayAgent |
device write |
Record the desired agent version and nudge the device. |
SetAgentChannel |
device write |
Move the device to another release channel. Does not start an update. |
Realtime tickets and console
| RPC |
Gate |
Purpose |
IssueViewerTicket |
device read |
Single-use ticket (30 s to redeem, 12 h session ceiling) for one viewer WebSocket. |
IssueShellTicket |
device console |
Single-use ticket (30 s to redeem, 1 h session ceiling) for one root-console WebSocket. ORG_OWNER, SUPPORT, SUPPORT_ADMIN and platform admins pass; ORG_MEMBER and PARTNER do not. Holding-zone devices are platform-admin only. |
ListShellSessions |
platform admin |
Console audit trail. |
Viewer and shell tickets live in separate tables and are validated by separate
RPCs, so one can never be redeemed as the other. See
Remote Shell.
Published services and relays
| RPC |
Gate |
Purpose |
IssuePublishedCode |
device write, or a segment grant covering the service's interface |
Ask the device to mint an access code for one published service. The code is returned once and never stored by nexus. Requires the device to be online. |
RevokePublishedCode |
as above |
Revoke one code by id, on the device. |
GetRelayLink |
device read |
The control plane's half of the gateway's relay path. Relay identity and addresses are filled for platform admins only. |
ListRelays |
platform admin |
The relay fleet. See Relay fleet. |
SetRelayDraining |
platform admin |
Drain or undrain a relay. |
PortalAdminService
Every RPC requires codes:create on portal:system (target id *), which
only the PLATFORM_ADMIN wildcard satisfies.
| RPC |
Purpose |
CreateBootstrapToken |
Mint a named, revocable, multi-use bootstrap token (max_uses 0 = unlimited). Not MAC-locked. The gateway provisioning path. |
CreateEnrollmentCodes |
Batch-create MAC-bound single-use codes (Monsieur Wifi AP claim). |
ListEnrollmentCodes / RevokeEnrollmentCode / DeleteEnrollmentCode |
Manage codes and tokens. |
ListDevices / DeleteDevice |
Every device in every org. |
ListOrganizations / DeleteOrganization |
Every org. |
CreateAgentRelease / ListAgentReleases / SetAgentReleaseYanked |
OTA release catalogue. |
| RPC |
Gate |
Purpose |
Invite |
invite authority |
Create a Zitadel user, or resend the invite if the email exists; returns {user_id, created}. Allowed for a platform admin or a caller holding grant authority somewhere (the org wildcard, or write/transfer on a device). ORG_MEMBER, PARTNER and users with no grant are refused. |
Search |
none |
Exact-match lookup of one whole email address. Partial input returns no users. The request's method field is ignored. |
ReleasePublisherService
| RPC |
Gate |
Purpose |
PublishAgentRelease |
X-Release-Key header |
Called by the edge/gateway release pipeline to register a signed build. Refused when NEXUS_RELEASE_PUBLISHER_KEY is unset. |
PortalLinkService
Site-to-site links between two gateways: CreateLink, GetLink, ListLinks,
DeleteLink, AdvanceLink. Every RPC gates on both devices. The handler
(internal/gwconnect/portal_link.go) and its tables (site_links,
site_link_ranges) exist, but the service is not mounted in
internal/app/app.go and the portal has no client for it, so these RPCs are
not reachable.
- Connect-JSON. The portal uses JSON by default (
VITE_NEXUS_BINARY=true
switches to binary). In JSON, int64 values are strings and bytes are
base64.
- Opaque
bytes fields carry JSON documents: state_data, config_data,
interfaces_from_newer_versions, metrics_json, preference value. The
generated client types them as Uint8Array; whatever the portal decodes from
them is untyped, so an int64 inside arrives as a string with no compiler
warning. Prefer the typed fields where they exist (GetGatewayResponse.state,
GetGatewayStateResponse.state, both gateway.v1.StateReportRequest).