# How INFINI Console Developer Tools Handles Workspace Management and Command Persistence

> Explore how INFINI Console's developer tools manage workspaces and persist commands. Learn about Layout documents, CommonCommand documents, and Elasticsearch integration for efficient development.

- Repository: [INFINI Labs/console](https://github.com/infinilabs/console)
- Tags: internals
- Published: 2026-03-04

---

**INFINI Console's Developer Tools module persists workspaces as Layout documents with type `"workspace"` via the Layout API, while storing reusable commands as `CommonCommand` documents in a dedicated Elasticsearch index, both exposed through RESTful endpoints with fine-grained permission controls.**

The [infinilabs/console](https://github.com/infinilabs/console) repository provides a comprehensive web interface for Elasticsearch operations, with the Developer Tools (DevTools) module enabling users to write, execute, and save queries. This article examines the dual-persistence architecture that powers workspace management and command storage, referencing the actual Go source files and React front-end components that implement these features.

## Workspace Management Through the Layout API

The Developer Tools module treats user workspaces as specialized dashboard layouts, leveraging a generic layout management system with a specific type discriminator.

### The Workspace Data Model

In [`model/layout.go`](https://github.com/infinilabs/console/blob/main/model/layout.go), the system defines a constant that identifies workspace layouts:

```go
const (
    LayoutTypeWorkspace = "workspace"
)

```

This constant appears at lines 49-51, distinguishing workspace layouts from other dashboard types. When a user creates a new workspace through the DevTools UI, the front-end sends a **POST `/layout`** request to the server. The handler creates a new `model.Layout` record, populates the `Creator` and `Created` fields, and explicitly sets `Type = LayoutTypeWorkspace` before persistence.

### CRUD Operations and Endpoint Registration

The Layout API implements full CRUD functionality in [`plugin/api/layout/layout.go`](https://github.com/infinilabs/console/blob/main/plugin/api/layout/layout.go). Route registration occurs in [`plugin/api/layout/api.go`](https://github.com/infinilabs/console/blob/main/plugin/api/layout/api.go) (lines 41-46), where HTTP verbs bind to handler functions:

```go
// Endpoints include:
// POST   /layout          - Create workspace
// GET    /layout/:id      - Retrieve workspace
// PUT    /layout/:id      - Update workspace
// DELETE /layout/:id      - Remove workspace

```

These handlers utilize the generic ORM helpers—`orm.Get`, `orm.Search`, `orm.Update`, and `orm.Delete`—to interact with the underlying Elasticsearch storage. Each operation validates permissions against the `PermissionLayoutRead` and `PermissionLayoutWrite` constants defined in [`core/security/enum/const.go`](https://github.com/infinilabs/console/blob/main/core/security/enum/const.go).

### Index Registration and Storage

The workspace data persists in the **`layout`** index, registered during application startup in [`main.go`](https://github.com/infinilabs/console/blob/main/main.go) at line 156. The registration binds the `model.Layout` struct to the Elasticsearch index, enabling the ORM layer to handle document serialization and query generation automatically.

## Command Persistence in Elasticsearch

While workspaces manage UI layouts, the command persistence layer handles the storage and retrieval of reusable Elasticsearch queries.

### The CommonCommand Document Structure

Commands are stored as `CommonCommand` documents, defined in the shared framework package (`infini.sh/framework/core/elastic.CommonCommand`). The system registers this schema with a custom index name in [`main.go`](https://github.com/infinilabs/console/blob/main/main.go) at line 146:

```go
orm.RegisterSchemaWithIndexName(elastic.CommonCommand{}, "commands")

```

This registration ensures all command documents route to the dedicated **`commands`** index, separate from the workspace layout storage.

### Command Lifecycle API Handlers

The file [`plugin/api/index_management/common_command.go`](https://github.com/infinilabs/console/blob/main/plugin/api/index_management/common_command.go) implements four primary handlers that manage the command lifecycle:

**`HandleAddCommonCommandAction`** (lines 40-81) processes **POST `/elasticsearch/command`** requests. The handler generates a UUID, timestamps the command, validates against duplicate titles, and indexes the document using `esClient.Index`.

**`HandleSaveCommonCommandAction`** (lines 86-127) handles **PUT `/elasticsearch/command/:cid`**. This endpoint overwrites existing documents by ID, preserving the command ID while updating the query content and metadata.

**`HandleQueryCommonCommandAction`** constructs a query DSL for **GET `/elasticsearch/command`**, returning the raw Elasticsearch search response with all saved commands for the current user context.

**`HandleDeleteCommonCommandAction`** (lines 130-150) executes **DELETE `/elasticsearch/command/:cid`**, removing the document from the index and invalidating the in-memory cache entry simultaneously.

### Security and Access Control

Route registration in [`plugin/api/init.go`](https://github.com/infinilabs/console/blob/main/plugin/api/init.go) (lines 65-68) binds these handlers to specific permission constants:

```go
// Permission checks enforced:
// - PermissionCommandRead  (for GET operations)
// - PermissionCommandWrite (for POST/PUT/DELETE operations)

```

These permissions, defined in [`core/security/enum/const.go`](https://github.com/infinilabs/console/blob/main/core/security/enum/const.go), ensure that only authorized users can modify the shared command repository while allowing broader read access for team collaboration.

## Front-End Integration and Developer Experience

The React-based front-end bridges user interactions with these back-end services through dedicated service layers and UI components.

### DevTools Settings Management

Editor preferences and DevTools configuration reside in [`web/src/components/vendor/console/services/settings.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/console/services/settings.ts). The `DevToolsSettings` interface (lines 22-30) defines:

```typescript
interface DevToolsSettings {
  fontSize: number;
  autocomplete: boolean;
  lineNumbers: boolean;
  theme: string;
  // Additional Monaco editor configurations
}

```

The component [`apply_editor_settings.ts`](https://github.com/infinilabs/console/blob/main/apply_editor_settings.ts) (lines 20-27) consumes these settings to configure the Monaco editor instance used for command editing, applying font sizes, themes, and autocomplete behavior dynamically.

### Workspace UI Components

Workspace creation and selection flow through the Dashboard pages in `web/src/pages/Insight/Dashboard/`. These React components:

1. Issue HTTP POST requests to `/layout` when users create new workspaces
2. Send layout metadata including `type: "workspace"`, name, and description
3. Handle workspace import/export through the same Layout API endpoints

When users click **"Save Command"** or **"Load Command"** in the DevTools interface, the front-end invokes the command API endpoints directly, persisting query text to the `commands` index and enabling cross-session reuse.

## Summary

- **Workspaces** are stored as `Layout` documents with `Type = "workspace"` in the `layout` index, managed by the Layout API in `plugin/api/layout/`.
- **Commands** persist as `CommonCommand` documents in the dedicated `commands` index, handled by `CommonCommand` API handlers in `plugin/api/index_management/`.
- **Permission controls** enforce access through `PermissionCommandRead/Write` and `PermissionLayoutRead/Write` constants.
- **Front-end integration** occurs via React components in `web/src/components/vendor/console/` and `web/src/pages/Insight/Dashboard/`, communicating with RESTful endpoints to provide seamless query management.

## Frequently Asked Questions

### What Elasticsearch indices store workspace and command data?

Workspaces reside in the **`layout`** index, registered in [`main.go`](https://github.com/infinilabs/console/blob/main/main.go) at line 156. Commands reside in the **`commands`** index, registered at line 146 via `orm.RegisterSchemaWithIndexName(elastic.CommonCommand{}, "commands")`.

### How does the system prevent duplicate command titles?

The `HandleAddCommonCommandAction` handler in [`plugin/api/index_management/common_command.go`](https://github.com/infinilabs/console/blob/main/plugin/api/index_management/common_command.go) (lines 40-81) validates the command title against existing documents before indexing. If a duplicate title exists within the user context, the API returns an error before the `esClient.Index` operation executes.

### Which permissions control access to Developer Tools features?

The system defines four relevant permissions in [`core/security/enum/const.go`](https://github.com/infinilabs/console/blob/main/core/security/enum/const.go): `PermissionCommandRead` and `PermissionCommandWrite` for query management, plus `PermissionLayoutRead` and `PermissionLayoutWrite` for workspace operations. These are enforced during route registration in [`plugin/api/init.go`](https://github.com/infinilabs/console/blob/main/plugin/api/init.go).

### Where is the Monaco editor configuration applied?

The `DevToolsSettings` interface in [`web/src/components/vendor/console/services/settings.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/console/services/settings.ts) stores editor preferences. The [`apply_editor_settings.ts`](https://github.com/infinilabs/console/blob/main/apply_editor_settings.ts) component (lines 20-27) applies these configurations to the Monaco editor instance, controlling font size, themes, and autocomplete behavior in the DevTools query interface.