Skip to content

view: toggle-first config UI + stable --token-file - #178

Merged
yourconscience merged 1 commit into
mainfrom
feat/webui-toggle-first
Sep 14, 2026
Merged

yourconscience merged 1 commit into
mainfrom
feat/webui-toggle-first

Conversation

@yourconscience

@yourconscience yourconscience commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Reworks the canonical config web UI (dotagents view) to be toggle-first, and adds a stable session token for persistent hosting.

UI

  • Each enable/disable toggle now applies immediately via a single-op PATCH /api/config on the canonical write path (revision-guarded), instead of staging into a Validate → Review-diff → Save change-rail.
  • Removed the raw-YAML editor, the change-rail, and the Settings button. One Sync now button keeps materialize-into-native-dirs explicit and separate; destructive sync items still require confirmation.
  • Header nav renders ui.links (e.g. an "AI usage" link). Effective layer stays read-only.

Server

  • New --token-file PATH for dotagents view: a stable session token (minted 0600 on first use) so a restarted persistent service keeps one bookmarkable URL. Without it, behavior is unchanged (fresh per-process token).

Tests / verify

  • config_web_token_test.go: ephemeral-by-default, stable-from-file (0600), flag parse. Existing single-op PATCH contract already covered.
  • Verified live in-browser: toggles persist to YAML instantly, revision advances across consecutive flips, Effective read-only, AI usage/usage. go test ./... + go vet green.

Note: PR #177's demo screenshots show the old change-rail/Save UI removed here and will need reshooting; mind merge order.

Summary by Sourcery

Streamline dotagents view around immediate configuration toggles and add optional stable session URLs for persistent hosting.

New Features:

  • Add an optional stable session token file for dotagents view, preserving its access URL across service restarts.
  • Apply configuration toggles immediately through the canonical configuration endpoint.
  • Provide a single explicit Sync now workflow with confirmation for destructive changes and retain configured header links.

Enhancements:

  • Simplify the configuration web UI by removing the raw YAML editor, staged change rail, validation/review/save controls, and Settings navigation.
  • Document the immediate-toggle behavior for the web configuration surface.

Documentation:

  • Update the dotagents skill documentation to describe immediate web UI changes and the separate sync step.

Tests:

  • Add coverage for ephemeral tokens, stable token-file persistence and permissions, and token-file flag parsing.

@sourcery-ai

sourcery-ai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Reworks dotagents view into an immediate toggle-first canonical config editor with an explicit, confirmation-aware Sync now flow, while adding optional 0600-backed stable session tokens via --token-file for persistent hosting.

Sequence diagram for immediate config toggle and sync

sequenceDiagram
    participant User
    participant UI as ConfigUI
    participant API as ConfigAPI
    participant YAML as CanonicalYAML
    participant Agents

    User->>UI: Toggle setting
    UI->>API: PATCH /api/config
    API->>YAML: Apply operation with expected_revision
    YAML-->>API: New revision
    API-->>UI: revision
    UI-->>User: Toggle applied, sync required
    User->>UI: Click Sync now
    UI->>API: POST /api/sync/preview
    API-->>UI: Sync plan
    alt Destructive changes
        UI-->>User: Request confirmation
        User->>UI: Confirm sync
    end
    UI->>API: POST /api/sync/apply
    API->>Agents: Materialize configuration
    Agents-->>API: Sync complete
    API-->>UI: Success
Loading

Sequence diagram for stable view session token

sequenceDiagram
    participant User
    participant CLI as dotagentsView
    participant Token as TokenFile
    participant Server as ConfigWebServer

    User->>CLI: dotagents view --token-file PATH
    CLI->>Token: Read PATH
    alt Existing non-empty token
        Token-->>CLI: Stable token
    else Missing token
        CLI->>CLI: randomToken
        CLI->>Token: Write token with mode 0600
        Token-->>CLI: Token persisted
    end
    CLI->>Server: Start with session token
    Server-->>User: Bookmarkable access URL
Loading

File-Level Changes

Change Details Files
Replace staged configuration editing with immediate, revision-guarded toggle updates and a dedicated sync workflow.
  • Send each toggle change as a single-operation PATCH with the current revision.
  • Reload state on stale revisions and revert failed toggles.
  • Remove validation, diff review, save, raw-YAML, and Settings UI while retaining read-only Effective configuration.
  • Combine sync preview, destructive confirmation, and apply under Sync now.
  • Render configured UI navigation links and update responsive styling/documentation.
cmd/dotagents/web/app.js
cmd/dotagents/web/index.html
cmd/dotagents/web/style.css
skills/dotagents/SKILL.md
Add an optional persistent session-token file for stable view URLs across service restarts.
  • Add and document the --token-file view flag.
  • Reuse a non-empty token from the specified file or mint and persist a new token with 0600 permissions.
  • Preserve fresh per-process tokens when no token file is configured.
  • Cover ephemeral behavior, file persistence and permissions, reuse, and flag parsing with tests.
cmd/dotagents/config_tui.go
cmd/dotagents/config_web.go
cmd/dotagents/config_web_token_test.go
cmd/dotagents/main.go
cmd/dotagents/view.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: c00b63a1-6e1a-4320-bd03-d3f08e0bd6cc


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@yourconscience
yourconscience merged commit 78c9d59 into main Sep 14, 2026
6 checks passed
@yourconscience
yourconscience deleted the feat/webui-toggle-first branch September 14, 2026 12:10

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="cmd/dotagents/config_web.go" line_range="124-127" />
<code_context>
+	if tokenFile == "" {
+		return randomToken(32)
+	}
+	switch data, err := os.ReadFile(tokenFile); {
+	case err == nil:
+		if tok := strings.TrimSpace(string(data)); tok != "" {
+			return tok, nil
+		}
+	case !errors.Is(err, os.ErrNotExist):
+		return "", fmt.Errorf("read token file %s: %w", tokenFile, err)
+	}
+	tok, err := randomToken(32)
+	if err != nil {
+		return "", err
+	}
+	if err := os.WriteFile(tokenFile, []byte(tok+"\n"), 0o600); err != nil {
+		return "", fmt.Errorf("write token file %s: %w", tokenFile, err)
+	}
</code_context>
<issue_to_address>
**🚨 issue (security):** When `tokenFile` already exists but is empty, `resolveServerToken` mints a token through `os.WriteFile` without changing the existing file mode, so an existing permissive mode such as `0644` remains in place despite the first-use token being documented as `0600`.

**Triggers:** When the configured token path is pre-created by deployment tooling or a previous failed initialization with permissions broader than `0600`.

**Suggested fix:** Open or rewrite the token file with explicit restrictive permissions and call `os.Chmod(tokenFile, 0o600)` after validating the file is the intended token file.

```suggestion
	if err := os.WriteFile(tokenFile, []byte(tok+"\n"), 0o600); err != nil {
		return "", fmt.Errorf("write token file %s: %w", tokenFile, err)
	}
	if err := os.Chmod(tokenFile, 0o600); err != nil {
		return "", fmt.Errorf("chmod token file %s: %w", tokenFile, err)
	}
	return tok, nil
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and the opt-in token file changes the lifetime of the bearer credential protecting the web UI; if token persistence or file handling is wrong, previously distributed URLs may continue granting access after a revert and cannot be revoked by rerunning the old code. The UI also now persists configuration on every toggle, while sync can propagate those changes to agents, although destructive sync still requires confirmation.

Blocking findings: cmd/dotagents/config_web.go:127


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines +124 to +127
if err := os.WriteFile(tokenFile, []byte(tok+"\n"), 0o600); err != nil {
return "", fmt.Errorf("write token file %s: %w", tokenFile, err)
}
return tok, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 issue (security): When tokenFile already exists but is empty, resolveServerToken mints a token through os.WriteFile without changing the existing file mode, so an existing permissive mode such as 0644 remains in place despite the first-use token being documented as 0600.

Triggers: When the configured token path is pre-created by deployment tooling or a previous failed initialization with permissions broader than 0600.

Suggested fix: Open or rewrite the token file with explicit restrictive permissions and call os.Chmod(tokenFile, 0o600) after validating the file is the intended token file.

Suggested change
if err := os.WriteFile(tokenFile, []byte(tok+"\n"), 0o600); err != nil {
return "", fmt.Errorf("write token file %s: %w", tokenFile, err)
}
return tok, nil
if err := os.WriteFile(tokenFile, []byte(tok+"\n"), 0o600); err != nil {
return "", fmt.Errorf("write token file %s: %w", tokenFile, err)
}
if err := os.Chmod(tokenFile, 0o600); err != nil {
return "", fmt.Errorf("chmod token file %s: %w", tokenFile, err)
}
return tok, nil

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 33360acbb8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +113 to +115
case err == nil:
if tok := strings.TrimSpace(string(data)); tok != "" {
return tok, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce private permissions on reused token files

On a multi-user host where an existing token file is group/world-readable, this branch returns its bearer token without checking or tightening its mode. Because that token can be exchanged for a session that invokes config mutation and sync APIs over loopback, another local user with access to the path can take control of the service; reject insecure/non-regular files or chmod them to 0600 before reuse.

Useful? React with 👍 / 👎.

Comment thread skills/dotagents/SKILL.md
Comment on lines +41 to +42
configuration is read-only. In `view`, each toggle applies immediately; neither
surface runs `sync` implicitly. `view` binds only to loopback and uses a session cookie plus

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Align all public view documentation with this behavior

This updates only the skill prose for immediate toggle writes: README.md:122-140 still promises a review-first save and preview/confirm workflow, while both README.md:89 and the command list at skills/dotagents/SKILL.md:28 omit the new --token-file option. Update those surfaces and the release-site copy so users are not given the obsolete workflow and can discover the persistent-service flag.

AGENTS.md reference: AGENTS.md:L51-L51

Useful? React with 👍 / 👎.

Comment thread cmd/dotagents/web/app.js
Comment on lines +107 to +110
input.disabled = true;
try {
const result = await api('/api/config/validate', {method:'POST', body:JSON.stringify({layer, operations:[...pendingOperations.values()]})});
$('#diff').textContent = result.diff || '(no changes)';
setStatus('Change staged. Review the diff, then save.', 'ok');
const result = await api('/api/config', {method:'PATCH', body:JSON.stringify({layer, expected_revision:state.revision, operations:[{op:'set', path, value}]})});
state.revision = result.revision;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Serialize toggle writes before accepting another change

When a user flips a second row before the first PATCH returns, both requests carry the same state.revision because only the clicked input is disabled. After one request advances the revision, every other in-flight toggle receives stale_revision and is reloaded away, so quickly enabling several agents, hooks, or servers applies only one of the requested changes; queue PATCHes or disable all editable controls until the revision is updated.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant