|
| 1 | +--- |
| 2 | +title: Meshery Server Registration |
| 3 | +description: > |
| 4 | + How a Meshery Server registers itself with Layer5 Cloud as its Remote Provider, what data is recorded, and how re-registration is handled. |
| 5 | +weight: 1 |
| 6 | +categories: [Concepts] |
| 7 | +tags: [connections, remote-provider, meshery-server] |
| 8 | +--- |
| 9 | + |
| 10 | +Layer5 Cloud is a [Meshery Remote Provider](https://docs.meshery.io/extensibility/providers). When a Meshery Server is configured to use Layer5 Cloud (SaaS or self-hosted), the server *registers itself* with Cloud on behalf of the authenticated user. Registration is what makes a particular Meshery Server visible inside Cloud — it appears as a `meshery` Connection on the user's account, scoped to their Organization, and becomes the anchor point for everything else the user does through that server (designs, environments, workspaces, events, capabilities). |
| 11 | + |
| 12 | +This page documents the registration behavior implemented by Layer5 Cloud: what the server sends, how Cloud identifies an existing registration vs a new one, and what fields are preserved across re-registration. |
| 13 | + |
| 14 | +## Why Registration Exists |
| 15 | + |
| 16 | +The Meshery Server / Remote Provider boundary has two requirements: |
| 17 | + |
| 18 | +1. **Identity** — Cloud needs to know *which* Meshery Server is calling, so multiple Meshery deployments can talk to the same Cloud tenant without colliding. |
| 19 | +2. **Ownership** — Every Meshery Server registration is owned by exactly one user (and therefore one Organization). All subsequent activity through that server is attributed to that user for audit, RBAC, and tenancy. |
| 20 | + |
| 21 | +A registration record satisfies both: it associates a unique server identifier with a user and an Organization, and it survives across Meshery Server restarts. |
| 22 | + |
| 23 | +## Identifying a Meshery Server |
| 24 | + |
| 25 | +A Meshery Server is identified by a stable `server_id` it generates on first start and persists locally. The `server_id` travels in the Connection's `metadata` payload. |
| 26 | + |
| 27 | +Cloud's uniqueness rule for an existing Meshery registration is the triple: |
| 28 | + |
| 29 | +``` |
| 30 | +(kind = 'meshery', user_id, metadata.server_id) |
| 31 | +``` |
| 32 | + |
| 33 | +The lookup is implemented in `ConnectionDAO.CheckMesheryServerExistence`. A given user can register many Meshery Servers, and the same Meshery Server can register for many users — but a given `(user, server_id)` pair maps to exactly one Connection row. |
| 34 | + |
| 35 | +## Registration Endpoint |
| 36 | + |
| 37 | +Meshery Server registers by `POST`ing a connection payload to: |
| 38 | + |
| 39 | +``` |
| 40 | +POST /api/integrations/connections |
| 41 | +``` |
| 42 | + |
| 43 | +The request must carry a valid Cloud session (Kratos browser session or JWT). The handler resolves the caller from session context and uses that user's ID as the owning `user_id`. |
| 44 | + |
| 45 | +Required payload fields: |
| 46 | + |
| 47 | +| Field | Description | |
| 48 | +|------------|-------------| |
| 49 | +| `kind` | Must be `meshery`. | |
| 50 | +| `metadata.server_id` | Stable UUID generated by the Meshery Server. | |
| 51 | + |
| 52 | +Optional payload fields: |
| 53 | + |
| 54 | +| Field | Description | |
| 55 | +|------------|-------------| |
| 56 | +| `name` | Human-readable display name. If omitted, Cloud generates `meshery-<random>`. | |
| 57 | +| `type` / `sub_type` | Free-form classification fields. | |
| 58 | +| `metadata` | Additional JSON metadata (e.g. server version, hostname). | |
| 59 | +| `status` | Initial status (defaults to the system's registered state). | |
| 60 | + |
| 61 | +The handler chain is `RegisterConnection` → `MesheryConnectionUtilityHandlerRegistration` → either `CreateConnection` or `UpdateConnection`. |
| 62 | + |
| 63 | +## What Happens on First Registration |
| 64 | + |
| 65 | +1. The handler validates that the resolved session has a non-nil `user_id`. The `connections.user_id` column is `NOT NULL`; a missing user at this point is a hard error and the request is rejected. |
| 66 | +2. `CheckMesheryServerExistence` looks for a row matching `(meshery, user_id, server_id)`. |
| 67 | +3. No match is found, so a new row is inserted via `CreateConnection`. |
| 68 | +4. The new Connection inherits the user's Organization for tenant isolation. |
| 69 | + |
| 70 | +## What Happens on Re-Registration |
| 71 | + |
| 72 | +A re-registration is any subsequent call from the same Meshery Server (same `server_id`) for the same user — typically after a Meshery Server restart, a new browser session, or a Cloud reconnect. |
| 73 | + |
| 74 | +The handler: |
| 75 | + |
| 76 | +1. Validates the session's `user_id` (same as first registration). |
| 77 | +2. Finds the existing row via `CheckMesheryServerExistence`. |
| 78 | +3. **Preserves the existing row's stable identity fields** — `name`, `kind`, `type`, `sub_type`, `status` — so a freshly-minted random name from a payload that omitted `name` cannot rename the existing Meshery Server, and missing `type` / `sub_type` cannot blank them. |
| 79 | +4. **Preserves the existing row's `created_at`**, sets `updated_at = now()`. |
| 80 | +5. **Preserves a previously-set `user_id`**: if the row already has an owner, the existing owner wins; if the row's `user_id` is somehow nil, the validated incoming `user_id` is kept (this guard exists because a historical narrow `SELECT id, metadata` projection in the DAO could leave the in-memory row's UserID nil, which would otherwise UPDATE the row to a null `user_id` and violate the NOT NULL constraint). |
| 81 | +6. Calls `UpdateConnection`, which updates every column except `status`. |
| 82 | + |
| 83 | +The net effect: re-registering a Meshery Server is **safe and idempotent** with respect to identity. Only `metadata` and `updated_at` are intended to change across re-registrations. |
| 84 | + |
| 85 | +{{< alert type="info" title="Why status is excluded from updates" >}} |
| 86 | +Connection status transitions are managed through dedicated lifecycle endpoints (e.g. connect / disconnect, ignore, delete), not through registration. A re-registration call cannot reset a previously-disconnected Meshery Server's status by accident. |
| 87 | +{{< /alert >}} |
| 88 | + |
| 89 | +## Validation and Error Cases |
| 90 | + |
| 91 | +| Condition | Behavior | |
| 92 | +|-----------|----------| |
| 93 | +| Session has no resolvable user | Registration is rejected before any DAO call. The caller sees an HTTP 500 with an error describing the missing `user_id`. | |
| 94 | +| `kind` is not in the supported list | HTTP 500 with an "unsupported connection kind" error. | |
| 95 | +| `server_id` is missing from `metadata` | The lookup degenerates to `metadata->>'server_id' IS NULL`, which will not match any prior registration; a fresh row will be created. Always send a stable `server_id`. | |
| 96 | +| DB error during lookup or write | Wrapped in a structured MeshKit error and returned to the caller. | |
| 97 | + |
| 98 | +## Operator Guidance |
| 99 | + |
| 100 | +If you operate a self-hosted Layer5 Cloud and see registration-related errors, the symptoms below map to the most common root causes: |
| 101 | + |
| 102 | +- **`Postgres 23502` / `meshery-server-1345`** during auth completion — the registration call reached the DAO with a nil `user_id`. This is now caught by the handler-level guard described above; if you see it on a build that pre-dates that guard, upgrade. If you see it on a post-guard build, the session resolution itself is failing — check the logs around the request for missing JWT / Kratos session context. |
| 103 | +- **A Meshery Server appears under the wrong user** — check whether the operator authenticated as a different account against the Cloud session. The `user_id` is taken from the session, not the payload. |
| 104 | +- **A Meshery Server's display name keeps changing** — pre-fix builds would rename existing rows when the payload omitted `name`. Upgrade to a build that preserves stable identity fields on re-registration (the behavior documented above). |
| 105 | + |
| 106 | +## Where the Code Lives |
| 107 | + |
| 108 | +- Handler: [`server/handlers/connections.go`](https://github.com/layer5io/meshery-cloud/blob/master/server/handlers/connections.go) — `RegisterConnection`, `MesheryConnectionUtilityHandlerRegistration` |
| 109 | +- DAO: [`server/dao/connections.go`](https://github.com/layer5io/meshery-cloud/blob/master/server/dao/connections.go) — `CheckMesheryServerExistence`, `CreateConnection`, `UpdateConnection` |
| 110 | +- Schema: [`meshery/schemas` — Connection](https://github.com/meshery/schemas) |
0 commit comments