Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions content/en/cloud/concepts/_index.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
---
title: Concepts
weight: 2
draft: true
description: >
An overview of Layer5 Cloud concepts and their relationships.
---

![concepts-overview](images/concepts-overview.svg "image-center-shadow")

<!--
{{% pageinfo %}}
Page under construction.
{{% /pageinfo %}}
This section explains the underlying concepts of Layer5 Cloud — the building blocks that the rest of the documentation assumes you understand.

For many projects, users may not need much information beyond the information in the [Overview](/docs/overview/), so this section is **optional**. However if there are areas where your users will need a more detailed understanding of a given term or feature in order to do anything useful with your project (or to not make mistakes when using it) put that information in this section. For example, you may want to add some conceptual pages if you have a large project with many components and a complex architecture.

Remember to focus on what the user needs to know, not just what you think is interesting about your project! If they don’t need to understand your original design decisions to use or contribute to the project, don’t put them in, or include your design docs in your repo and link to them. Similarly, most users will probably need to know more about how features work when in use rather than how they are implemented. Consider a separate architecture page for more detailed implementation and system design information that potential project contributors can consult.


{{ $context := . }}
{{ range $taxo, $taxo_map := .Site.Taxonomies }}
{{ partial "taxonomy_terms_article.html" (dict "context" $context "taxo" $taxo ) }}
{{ end }} -->
- [Meshery Server Registration](meshery-server-registration) — How a Meshery Server registers itself with Layer5 Cloud as its Remote Provider, how Cloud identifies an existing registration, and what fields are preserved across re-registration.
110 changes: 110 additions & 0 deletions content/en/cloud/concepts/meshery-server-registration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: Meshery Server Registration
description: >
How a Meshery Server registers itself with Layer5 Cloud as its Remote Provider, what data is recorded, and how re-registration is handled.
weight: 1
categories: [Concepts]
tags: [connections, remote-provider, meshery-server]
---

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).

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.

## Why Registration Exists

The Meshery Server / Remote Provider boundary has two requirements:

1. **Identity** — Cloud needs to know *which* Meshery Server is calling, so multiple Meshery deployments can talk to the same Cloud tenant without colliding.
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.

A registration record satisfies both: it associates a unique server identifier with a user and an Organization, and it survives across Meshery Server restarts.

## Identifying a Meshery Server

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.

Cloud's uniqueness rule for an existing Meshery registration is the triple:

```
(kind = 'meshery', user_id, metadata.server_id)
```

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.

## Registration Endpoint

Meshery Server registers by `POST`ing a connection payload to:

```
POST /api/integrations/connections
```

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`.

Required payload fields:

| Field | Description |
|------------|-------------|
| `kind` | Must be `meshery`. |
| `metadata.server_id` | Stable UUID generated by the Meshery Server. |

Optional payload fields:

| Field | Description |
|------------|-------------|
| `name` | Human-readable display name. If omitted, Cloud generates `meshery-<random>`. |
| `type` / `sub_type` | Free-form classification fields. |
| `metadata` | Additional JSON metadata (e.g. server version, hostname). |
| `status` | Initial status (defaults to the system's registered state). |

The handler chain is `RegisterConnection` → `MesheryConnectionUtilityHandlerRegistration` → either `CreateConnection` or `UpdateConnection`.

## What Happens on First Registration

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.
2. `CheckMesheryServerExistence` looks for a row matching `(meshery, user_id, server_id)`.
3. No match is found, so a new row is inserted via `CreateConnection`.
4. The new Connection inherits the user's Organization for tenant isolation.

## What Happens on Re-Registration

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.

The handler:

1. Validates the session's `user_id` (same as first registration).
2. Finds the existing row via `CheckMesheryServerExistence`.
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.
4. **Preserves the existing row's `created_at`**, sets `updated_at = now()`.
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).
6. Calls `UpdateConnection`, which updates every column except `status`.

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.

{{< alert type="info" title="Why status is excluded from updates" >}}
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.
{{< /alert >}}

## Validation and Error Cases

| Condition | Behavior |
|-----------|----------|
| 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`. |
| `kind` is not in the supported list | HTTP 500 with an "unsupported connection kind" error. |
Comment on lines +93 to +94
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The documentation lists HTTP 500 for 'Session has no resolvable user' and 'unsupported connection kind'. These scenarios typically correspond to 401 Unauthorized and 400 Bad Request, respectively. If the server implementation currently returns 500, it should be noted that this is a known implementation detail, or ideally, the implementation should be updated to use standard 4xx status codes and the documentation adjusted to match.

| `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`. |
Comment on lines +50 to +95
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is an inconsistency between the definition of metadata.server_id as a Required field (line 50) and its described behavior when missing (line 95). If the API creates a fresh row instead of rejecting the request when server_id is absent, the field is technically optional for the request to succeed.

Consider moving metadata.server_id to the Optional payload fields table with a note that it is required for identity persistence, or update the behavior on line 95 to reflect a rejection if the API is intended to enforce its presence.

| DB error during lookup or write | Wrapped in a structured MeshKit error and returned to the caller. |

## Operator Guidance

If you operate a self-hosted Layer5 Cloud and see registration-related errors, the symptoms below map to the most common root causes:

- **`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.
- **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.
- **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).

## Where the Code Lives

- Handler: [`server/handlers/connections.go`](https://github.com/layer5io/meshery-cloud/blob/master/server/handlers/connections.go) — `RegisterConnection`, `MesheryConnectionUtilityHandlerRegistration`
- DAO: [`server/dao/connections.go`](https://github.com/layer5io/meshery-cloud/blob/master/server/dao/connections.go) — `CheckMesheryServerExistence`, `CreateConnection`, `UpdateConnection`
- Schema: [`meshery/schemas` — Connection](https://github.com/meshery/schemas)
Loading