Stellar ControlMission control · by Stellar Systems v0.1.0

Tools

Web Editor

Edit the configuration repository in the browser, on drafts reviewed as merge or pull requests, with the language server and dry runs.

The web editor lets people who do not use Git change catalogues, topology, steps and procedures from the web console. Each change is a draft: a branch and a working copy of its own, with the language server running on it and dry runs of its procedures. A draft reaches the configuration only through a review on the forge (a GitLab merge request or a GitHub pull request): the editor never writes to the target branch.

sequenceDiagram
    actor U as Operator
    participant W as Web console
    participant A as API (/v1/editor relay)
    participant E as stellar-editor
    participant G as Git remote / forge
    U->>W: Start a draft
    W->>A: POST /v1/editor/drafts
    A->>E: relayed
    E->>G: fetch the target branch
    E->>E: branch editor/<user>/<id>, working copy
    U->>W: edit files (LSP over WebSocket)
    U->>W: Dry run…
    W->>E: POST …/dry-run (stellar run --dry)
    U->>W: Propose for review
    E->>G: commit (author: the user), push, open or update the review
    G-->>G: review, approvals, merge

Architecture#

The editor is a service of its own, stellar-editor, separate from the API so that the API holds neither Git nor forge credentials. The API relays /v1/editor and everything under it, HTTP and WebSocket, to the address of api.editor_url, with the same identity headers; the web application reaches it on the same origin.

Relay errorStatusWhy
editor::disabled404No api.editor_url: no editor behind this API
editor::unreachable502The editor service does not answer
editor::too-large413A request body over 16 MiB

The all-in-one binary stellar-mcs runs the editor when editor.repository is set.

Configuration#

YAML
api:
  editor_url: http://127.0.0.1:8090
editor:
  listen: 127.0.0.1:8090
  workdir: /var/lib/stellar/editor
  repository: https://gitlab.example.org/ops/mcs-config.git
  branch: main
  path: .
  forge:
    kind: gitlab                       # gitlab, github, none or direct
    api: https://gitlab.example.org/api/v4
    project: ops/mcs-config
    token_env: STELLAR_FORGE_TOKEN
  cli: stellar
  declared_identity: true
KeyDefaultMeaning
editor.listen127.0.0.1:8090Listen address of the service
editor.workdir/var/lib/stellar/editorIts clone of the repository and the working copies of the drafts
editor.repositoryunsetGit remote of the configuration repository (a URL or a path)
editor.branchmainBranch the reviews target, and drafts start from
editor.path.The configuration repository within the Git repository (examples/config)
editor.forge.kindnonegitlab: merge requests; github: pull requests; none: branches pushed, no review opened; direct: no review, a proposed draft merged and published at once (a sandbox)
editor.forge.apihttps://gitlab.com/api/v4 or https://api.github.comAPI of a self-hosted forge
editor.forge.project''group/repository (GitLab) or owner/repository (GitHub)
editor.forge.token_envSTELLAR_FORGE_TOKENEnvironment variable holding the service token of the MCS
editor.clistellarThe stellar command, for dry runs
editor.declared_identitytrueWhether declared identities are accepted; false: OIDC tokens only

The service token of the MCS pushes the branches and opens the reviews; give it the rights to push branches and open merge or pull requests, and protect the target branch so that only a reviewed merge enters it. In a cell, it is the secret forge_token (stellar-admin cell secret <cell> forge_token).

Drafts#

  • Start. A draft has a title (the title of its review). It is a branch editor/<user>/<id>, started from the target branch as it is on the remote at that moment, in a working copy of its own.
  • Ownership. Only the user who started a draft may change it, propose it or abandon it; everyone else sees it read-only.
  • Files. Reading, writing and deleting files stay within the configuration repository of the working copy: never in .git, never outside.
  • Changes. The draft lists the files it adds, modifies and deletes against the target branch, and shows their diff.
  • Abandon. Removes the working copy and the local branch; a review already opened stays on the forge.

Language server#

Each open draft runs stellar-lsp on its working copy, over a WebSocket (/v1/editor/drafts/{id}/lsp). The web console uses CodeMirror 6 and its official LSP client: live diagnostics, completion by role, hover and definition, exactly as in VS Code. The editor accepts only file URIs within the draft: a message naming any other file is refused.

Dry runs#

Dry run… runs the procedure under the cursor after asking for its inputs, or a run request of the draft: the service saves the request, runs stellar run --dry on the working copy (at the speed chosen, 0.1 to 100), and streams its output as it comes, ending with exit <code>. At most two dry runs go at once (429 editor::busy otherwise); each starts its own local NATS server and components, so the host needs nats-server. See Simulated Targets.

Review#

Propose for review commits the changes of the draft, pushes its branch and opens the review, or finds the one already open, which the push updates. The commits are authored by the user (OIDC or declared identity) and committed by the MCS (Stellar Control editor); the description of the review says who proposed it. With forge.kind: none, the branch is pushed and nothing else.

Without review: direct#

A sandbox of one user, such as the cell of an online trial, has no forge and nobody to review: with forge.kind: direct, Publish makes the draft the current configuration at once.

  1. The configuration of the draft is compiled (stellar compile): when it does not compile, nothing else happens and the errors come back (422 editor::invalid).
  2. Its changes are committed as the user and merged into the target branch as it is now (the drafts published meanwhile included); a conflict with them stops here.
  3. The target branch is pushed, and the merged configuration compiled again and published to the NATS server of the editor (nats.url, its credentials and TLS): the reconciler applies it, and its journal names the user as its publisher.

The draft then shows its publication (the commit of the target branch and the snapshot) and starts again from there. The remote takes pushes to its checked-out branch in a cell (receive.denyCurrentBranch=updateInstead), so its files, which the next start of the cell compiles, follow.

Identity#

The editor identifies its users like the API: a checked OIDC token first (Authorization: Bearer, against auth.jwks_file), else a declared identity (X-Stellar-User) unless editor.declared_identity: false.

CodeStatusWhy
auth::invalid-token401Token refused
auth::no-provider401A token, but no auth.jwks_file
auth::token-required401A declared identity, refused by editor.declared_identity: false
auth::anonymous401No identity

A WebSocket opened by a browser cannot carry headers: the identity then comes in its query (?token=… or ?user=…).

HTTP routes#

The routes of the editor, under /v1/editor behind the API. Errors have the body of the API ({"errors": [{code, message}]}).

RouteEffect
GET /v1/editorTarget branch, forge kind and project
GET /v1/editor/draftsEvery draft, newest first
POST /v1/editor/draftsStarts a draft: {"title"}
GET /v1/editor/drafts/{id}A draft, its files and its changes
DELETE /v1/editor/drafts/{id}Abandons a draft
GET, PUT, DELETE /v1/editor/drafts/{id}/files/{path}Reads, writes (plain text body) or deletes a file
GET /v1/editor/drafts/{id}/diffIts diff against the target branch
POST /v1/editor/drafts/{id}/reviewProposes it for review: {"message"} optional
POST /v1/editor/drafts/{id}/dry-run?speed=XDry run of the run request of the body (YAML), output streamed
GET /v1/editor/drafts/{id}/lspThe language server of the draft, over WebSocket

Stellar Control · v0.1.0

↑↓ to moveEnter to open