Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a93bd58
fix(quickstart): keep Canvas working post first workspace + hide SaaS…
HongmingWang-Rabbit Apr 23, 2026
dae7f50
fix(wsauth): extend dev-mode escape hatch to WorkspaceAuth
HongmingWang-Rabbit Apr 23, 2026
96cc4b0
fix(quickstart): wire up template/plugin registry via manifest.json
HongmingWang-Rabbit Apr 23, 2026
539e348
fix(provisioner): force linux/amd64 pull + create on Apple Silicon ho…
HongmingWang-Rabbit Apr 23, 2026
47d3ef5
refactor(middleware): extract dev-mode fail-open predicate
HongmingWang-Rabbit Apr 23, 2026
de99a22
fix(quickstart): hotfixes discovered during live testing session
HongmingWang-Rabbit Apr 23, 2026
19cd5c9
test(router): set ADMIN_TOKEN in TestTestTokenRoute_RequiresAdminAuth…
HongmingWang-Rabbit Apr 23, 2026
2baaa97
feat(quickstart): default new agents to T3 (Privileged)
HongmingWang-Rabbit Apr 23, 2026
a0ac72f
test(canvas): update a11y tests for T3 default tier
HongmingWang-Rabbit Apr 23, 2026
5eb5e38
fix(canvas): re-centre Toolbar on canvas area when SidePanel is open
HongmingWang-Rabbit Apr 23, 2026
b4719ad
fix(canvas): Legend avoids TemplatePalette + silence WS handshake races
HongmingWang-Rabbit Apr 23, 2026
03b56fa
fix(canvas): collapse Org Templates section by default in palette
HongmingWang-Rabbit Apr 23, 2026
baa7e15
feat(canvas): provider-picker MissingKeysModal for multi-provider run…
HongmingWang-Rabbit Apr 23, 2026
c5bcd72
Merge remote-tracking branch 'origin/staging' into fix/restore-quicks…
HongmingWang-Rabbit Apr 23, 2026
427b764
chore: remove internal content + add hard CI gate (CEO directive 2026…
HongmingWang-Rabbit Apr 23, 2026
3456bf7
Merge pull request #1931 from Molecule-AI/chore/remove-internal-conte…
HongmingWang-Rabbit Apr 24, 2026
dc50a1c
refactor(canvas): data-drive provider picker from template config.yaml
HongmingWang-Rabbit Apr 24, 2026
e337efe
fix(canvas): propagate runtime through WORKSPACE_PROVISIONING event
HongmingWang-Rabbit Apr 24, 2026
18ebb1d
fix(server): remove 60s A2A client timeout + correct file-read cat args
HongmingWang-Rabbit Apr 24, 2026
1c60869
Merge remote-tracking branch 'origin/staging' into fix/restore-quicks…
Apr 24, 2026
9813d29
Merge pull request #1897 from Molecule-AI/fix/restore-quickstart-plus…
HongmingWang-Rabbit Apr 24, 2026
b3da0b2
fix(e2e): hermes cold-boot tolerance — 20min deadline + treat failed …
Apr 23, 2026
307b5b5
Merge pull request #1930 from Molecule-AI/fix/e2e-hermes-boot-timeout
HongmingWang-Rabbit Apr 24, 2026
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ PLUGINS_DIR= # Path to plugins/ directory (default: /plugins i
# MOLECULE_MCP_ALLOW_SEND_MESSAGE= # Set to "true" to include send_message_to_user in the MCP bridge tool list (issue #810). Excluded by default to prevent unintended WebSocket pushes from CLI sessions.
# MOLECULE_MCP_URL=http://localhost:8080 # Platform URL for opencode MCP config (opencode.json). Same as PLATFORM_URL; separate var so opencode configs can reference it without ambiguity.
# WORKSPACE_DIR= # Optional global host path bind-mounted to /workspace in every container. Per-workspace workspace_dir column overrides this; if neither is set each workspace gets an isolated Docker named volume.
# MOLECULE_ENV=development # Environment label (development/staging/production). Used for log tagging and conditional behaviour.
MOLECULE_ENV=development # Environment label (development/staging/production). Used for log tagging and for the AdminAuth dev-mode escape hatch (lets the Canvas dashboard keep working after the first workspace is created, when ADMIN_TOKEN is unset). SaaS deployments MUST set MOLECULE_ENV=production.
# MOLECULE_ENABLE_TEST_TOKENS= # Set to 1 to expose GET /admin/workspaces/:id/test-token (mints a fresh bearer token for E2E scripts). The route is auto-enabled when MOLECULE_ENV != production; this flag is the explicit override. Leave unset/0 in prod — the route 404s unless enabled.
# MOLECULE_ORG_ID= # SaaS only: org UUID set by control plane on tenant machines. When set, workspace provisioning auto-routes through the control plane API instead of Docker.
# CP_PROVISION_URL= # Override control plane URL for workspace provisioning (default: https://api.moleculesai.app). Only needed for testing against a non-production control plane.
Expand Down
95 changes: 95 additions & 0 deletions .github/workflows/block-internal-paths.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Block internal-flavored paths

# Hard CI gate. Internal content (positioning, competitive briefs, sales
# playbooks, PMM/press drip, draft campaigns) lives in Molecule-AI/internal —
# this public monorepo must never re-acquire those paths. CEO directive
# 2026-04-23 after a fleet-wide audit found 79 internal files leaked here.
#
# Failure mode without this gate: agents (PMM, Research, DevRel, Sales) drop
# briefs into the easiest path their cwd resolves to (root /research,
# /marketing, /docs/marketing) and gitignore alone won't catch a `git add -f`
# or a stale gitignore line. This workflow is the mechanical backstop.

on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main, staging]

jobs:
check:
name: Block forbidden paths
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # need previous commit to diff against on push events

- name: Refuse if forbidden paths appear
run: |
# Paths that must NEVER live in the public monorepo. Add to this
# list narrowly — broader patterns belong in .gitignore so day-to-day
# docs work isn't accidentally blocked.
FORBIDDEN_PATTERNS=(
"^research/"
"^marketing/"
"^docs/marketing/"
"^comment-[0-9]+\.json$"
"^test-pmm.*\.(txt|md)$"
"^tick-reflections.*\.(txt|md)$"
".*-temp\.(md|txt)$"
)

# Determine the diff base.
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
else
BASE="${{ github.event.before }}"
HEAD="${{ github.event.after }}"
fi

# Files added or modified in this change.
if [ -z "$BASE" ] || echo "$BASE" | grep -qE '^0+$'; then
# New branch / no previous SHA — check entire tree.
CHANGED=$(git ls-tree -r --name-only HEAD)
else
CHANGED=$(git diff --name-only --diff-filter=AM "$BASE" "$HEAD")
fi

if [ -z "$CHANGED" ]; then
echo "No changed files to inspect."
exit 0
fi

OFFENDING=""
for path in $CHANGED; do
for pattern in "${FORBIDDEN_PATTERNS[@]}"; do
if echo "$path" | grep -qE "$pattern"; then
OFFENDING="${OFFENDING}${path} (matched: ${pattern})\n"
break
fi
done
done

if [ -n "$OFFENDING" ]; then
echo "::error::Forbidden internal-flavored paths detected:"
printf "$OFFENDING"
echo ""
echo "These paths belong in Molecule-AI/internal, not this public repo."
echo "See docs/internal-content-policy.md for canonical locations."
echo ""
echo "If your file is genuinely public-facing (e.g. a blog post"
echo "ready to ship), use one of these alternatives instead:"
echo " • Public-bound blog posts: docs/blog/<slug>.md"
echo " • Public-bound tutorials: docs/tutorials/<slug>.md"
echo " • Public devrel content: docs/devrel/<slug>.md"
echo ""
echo "If you legitimately need to add a new top-level path that"
echo "happens to match a forbidden pattern, edit"
echo ".github/workflows/block-internal-paths.yml and update the"
echo "FORBIDDEN_PATTERNS list with reviewer signoff."
exit 1
fi

echo "✓ No forbidden paths in this change."
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,29 @@ backups/
# org-templates live in Molecule-AI/molecule-ai-org-template-* repos
# (including molecule-dev — no checkin exception).
# plugins live in Molecule-AI/molecule-ai-plugin-* repos.
# All three directories are populated by scripts/clone-manifest.sh
# (now auto-run by infra/scripts/setup.sh). The in-tree exception for
# molecule-dev was removed because the checked-in copy drifted from
# the standalone repo and shipped with broken !include references to
# role files that never existed in the snapshot.
/org-templates/
/plugins/
/workspace-configs-templates/
# Cloned by publish-workspace-server-image.yml so the Dockerfile's
# replace-directive path resolves. Lives in its own repo.
/molecule-ai-plugin-github-app-auth/

# Internal-flavored content lives in Molecule-AI/internal — NEVER in this
# public monorepo. Migrated 2026-04-23 (CEO directive). The CI workflow
# .github/workflows/block-internal-paths.yml enforces this; this gitignore
# is the second line of defence so accidental local writes don't reach a
# commit. See docs/internal-content-policy.md for the full rationale.
/research/
/marketing/
/docs/marketing/
# Common temp/scratch patterns agents have produced
/comment-*.json
*-temp.md
*-temp.txt
/test-pmm-*.txt
/tick-reflections-*.md
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ development workflow, conventions, and how to get your changes merged.
- **Python 3.11+** — workspace runtime
- **Docker** — infrastructure services (Postgres, Redis)
- **Git** — with hooks path set to `.githooks`
- **jq** — parses `manifest.json` during `setup.sh` to clone the
template/plugin registry. Install via `brew install jq` (macOS) or
`apt install jq` (Debian). Without it, setup.sh prints a note and
leaves the registry dirs empty (recoverable by installing jq and
re-running).

### Setup

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ cp .env.example .env
# and Temporal (:7233 gRPC, :8233 UI) on the shared
# `molecule-monorepo-net` Docker network. Temporal runs with
# no auth on localhost — dev-only; production must gate it.
#
# Also populates the template/plugin registry by cloning every repo
# listed in manifest.json into workspace-configs-templates/,
# org-templates/, and plugins/. Requires jq — install via
# `brew install jq` (macOS) or `apt install jq` (Debian). Idempotent:
# re-runs skip any target dir that's already populated.

cd workspace-server
go run ./cmd/server # applies pending migrations on first boot
Expand Down
5 changes: 5 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ cp .env.example .env
# 以及 Temporal (:7233 gRPC, :8233 UI),全部挂在共享的
# `molecule-monorepo-net` Docker 网络上。Temporal 默认无鉴权,
# 仅用于本地开发;生产环境必须加 mTLS / API Key。
#
# 同时会根据 manifest.json 拉取所有模板/插件仓库到
# workspace-configs-templates/、org-templates/、plugins/ 三个目录。
# 需要安装 jq:`brew install jq`(macOS)或 `apt install jq`(Debian)。
# 脚本幂等:已经存在内容的目录会被跳过,可以安全重跑。

cd workspace-server
go run ./cmd/server # 首次启动会自动跑 schema_migrations 里未应用的迁移
Expand Down
12 changes: 12 additions & 0 deletions canvas/src/components/CookieConsent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { useEffect, useState } from "react";
import { isSaaSTenant } from "@/lib/tenant";

const STORAGE_KEY = "molecule_cookie_consent";

Expand Down Expand Up @@ -74,7 +75,18 @@ export function CookieConsent() {
// Read persisted decision on mount. useState's initialState can't run
// on first render because localStorage is SSR-unsafe — defer to
// useEffect so the initial HTML is identical to the server snapshot.
//
// The banner is SaaS-only: it carries a link to the hosted
// privacy policy (moleculesai.app/legal/privacy) and presumes
// GDPR/ePrivacy obligations that only apply to the hosted offering.
// Self-hosted / local-dev / Vercel-preview hosts get no banner —
// matches the `isSaaSTenant()` convention used by AuthGate and
// the tier picker.
useEffect(() => {
if (!isSaaSTenant()) {
setVisible(false);
return;
}
setVisible(getStoredConsent() === null);
}, []);

Expand Down
8 changes: 7 additions & 1 deletion canvas/src/components/CreateWorkspaceDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,13 @@ export function CreateWorkspaceButton() {
],
[isSaaS],
);
const defaultTier = isSaaS ? 4 : 1;
// T3 ("Privileged") is the self-hosted default — gives agents the
// read_write workspace mount + Docker daemon access most templates
// expect to do real work. T1 sandboxed and T2 standard are kept as
// explicit opt-ins for low-trust agents. SaaS still defaults to T4
// because every SaaS workspace gets its own EC2 (sibling VMs, no
// shared blast radius — see isSaaSTenant() / tier picker hide logic).
const defaultTier = isSaaS ? 4 : 3;
const [tier, setTier] = useState(defaultTier);

// Refs for roving tabIndex on the tier radio group (WCAG 2.1 arrow-key nav)
Expand Down
8 changes: 7 additions & 1 deletion canvas/src/components/Legend.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
"use client";

import { STATUS_CONFIG } from "@/lib/design-tokens";
import { useCanvasStore } from "@/store/canvas";

const LEGEND_STATUSES = ["online", "provisioning", "degraded", "failed", "paused", "offline"] as const;

export function Legend() {
// TemplatePalette (when open) is fixed top-0 left-0 w-[280px] — the
// default bottom-6 left-4 position of this legend would sit under it.
// Shift past the 280 px palette + a 16 px gap when the palette is open.
const paletteOpen = useCanvasStore((s) => s.templatePaletteOpen);
const leftClass = paletteOpen ? "left-[296px]" : "left-4";
return (
<div className="fixed bottom-6 left-4 z-30 bg-zinc-900/95 border border-zinc-700/50 rounded-xl px-4 py-3 shadow-xl shadow-black/30 backdrop-blur-sm max-w-[280px]">
<div className={`fixed bottom-6 ${leftClass} z-30 bg-zinc-900/95 border border-zinc-700/50 rounded-xl px-4 py-3 shadow-xl shadow-black/30 backdrop-blur-sm max-w-[280px] transition-[left] duration-200`}>
<div className="text-[11px] font-semibold text-zinc-400 uppercase tracking-wider mb-2">Legend</div>

{/* Status */}
Expand Down
Loading
Loading