Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
efdcbeb
chore: refresh Edition profile website artifacts
a19q3 Jul 31, 2026
305d0f9
feat: ship live production registry pages
a19q3 Jul 31, 2026
6b9c691
feat: add explicit Registry namespace claim
a19q3 Jul 31, 2026
711c552
ops: add hardened production website deployment
a19q3 Jul 31, 2026
415239c
fix: rebuild Registry workflows and responsive layout
a19q3 Aug 1, 2026
b21f949
fix: simplify Registry wallet submission flow
a19q3 Aug 1, 2026
6a18af4
fix: stabilise Registry submission ergonomics
a19q3 Aug 1, 2026
8bf0832
fix: persist Registry header across routes
a19q3 Aug 1, 2026
b0e449b
fix: refine Registry visual hierarchy
a19q3 Aug 1, 2026
f528498
fix: expose supported CKB wallet choices
a19q3 Aug 1, 2026
b0c605d
fix: expose complete CKB wallet directory
a19q3 Aug 1, 2026
1834217
fix: add official wallet icons
a19q3 Aug 1, 2026
0925e59
fix: make registry wallet chooser mainnet only
a19q3 Aug 1, 2026
892cbbe
feat: unify registry artifact experience
a19q3 Aug 1, 2026
a59d2f5
feat: complete Registry artifact UI
a19q3 Aug 1, 2026
db0cb25
fix: keep directory redirects on HTTPS
a19q3 Aug 1, 2026
151c66f
feat: refine Registry workflows and interface
a19q3 Aug 2, 2026
df7cae3
feat: add Registry evidence task flows
a19q3 Aug 2, 2026
8de41bc
fix: align Registry UI with evidence model
a19q3 Aug 2, 2026
2a662ae
feat: add isolated Pudge Registry sandbox
a19q3 Aug 2, 2026
cc9a442
fix: use canonical Pudge registry domain
a19q3 Aug 2, 2026
b248529
fix: stabilize registry layout and site theme
a19q3 Aug 3, 2026
25bf71b
fix: handle empty registry states
a19q3 Aug 3, 2026
f297b72
fix registry publishing UX and state model
a19q3 Aug 5, 2026
3536a59
Close registry publishing workflow
a19q3 Aug 5, 2026
646ad55
Complete registry authorisation UX
a19q3 Aug 5, 2026
72f67de
Make Registry publishing session-first
a19q3 Aug 5, 2026
0f54db4
Refine Registry publishing layout
a19q3 Aug 5, 2026
d27f70a
Refine Registry API layout
a19q3 Aug 5, 2026
7cb3687
Improve site visual semantics and readability
a19q3 Aug 5, 2026
75cdfde
Refine Registry publishing workspace
a19q3 Aug 5, 2026
64775df
Fix Registry route coordination
a19q3 Aug 5, 2026
9b93398
Redesign navigation and harden Registry loading
a19q3 Aug 6, 2026
79fc554
Polish topbar language and source affordances
a19q3 Aug 6, 2026
6529b3e
Humanize Registry guidance and recovery
a19q3 Aug 6, 2026
a9a400f
Fix Registry filter menus
a19q3 Aug 6, 2026
ec1f4e6
Unify site alignment frame
a19q3 Aug 6, 2026
bfb831d
Refine Playground studio layout
a19q3 Aug 6, 2026
9866b49
Turn Playground into a recoverable Cell workbench
a19q3 Aug 6, 2026
a2c5600
Unify interactive control styling
a19q3 Aug 6, 2026
523977b
Refine site micro visual system
a19q3 Aug 6, 2026
abcce84
Accelerate Playground startup
a19q3 Aug 6, 2026
ea2492e
Add 0.25 Playground verification surface
a19q3 Aug 12, 2026
7dfde06
Align Registry release interfaces across networks
a19q3 Aug 14, 2026
9304d87
Give LS-IDL a canonical Registry route
a19q3 Aug 14, 2026
b2990a2
Give each Registry tab its own header
a19q3 Aug 14, 2026
37b8817
Redesign Registry hero and route layout
a19q3 Aug 14, 2026
d166618
Align Registry routes to shared grid
a19q3 Aug 14, 2026
4d8cb0d
Refine Registry visual hierarchy
a19q3 Aug 14, 2026
45663af
Add visual language to the Registry index
a19q3 Aug 14, 2026
94214ff
Refine Registry visual language
a19q3 Aug 14, 2026
03d30fa
Unify site material and Gestalt system
a19q3 Aug 14, 2026
65512df
Revert "Unify site material and Gestalt system"
a19q3 Aug 14, 2026
2e40955
Refine site substrate material
a19q3 Aug 14, 2026
d7732cd
Restore homepage brand animation
a19q3 Aug 14, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
node_modules/
dist/
dist-testnet/
.astro/
.cap/
deploy/.env
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ The static architecture is intentional. Most pages are generated from committed

- A first-page overview of CellScript and its CKB contract workflow.
- A registry browser backed by generated package metadata.
- A Registry submit flow whose compact chooser includes the complete official
CKB wallet directory: detected CCC CKB signers connect in-browser, while
other wallets hand off to the same verifiable external-signature contract.
- A playground page with the current web-facing compiler assets.
- Learning and documentation entry points.
- Design and audit notes under `docs/`.
Expand Down Expand Up @@ -46,6 +49,39 @@ docs can be embedded:
CELLSCRIPT_REPO_ROOT=/path/to/CellScript npm run build
```

The wallet directory is deliberately separate from runtime signer discovery.
All twelve official CKB wallet entries remain visible; a wallet is labelled as
direct only when CCC exposes a compatible CKB signer. Other entries open the
wallet's official surface and accept a complete `wallet-signature.json`
handoff. The Registry never accepts recovery phrases, and the API applies the
same public-key, canonical-challenge, and recoverable-signature checks to both
paths. Registry wallet authorisation is mainnet-only; the chooser does not
offer a network switch or construct a testnet CCC client.

Wallet marks under `public/wallets/` are the official SVG assets published by
the Nervos CKB documentation wallet directory. They are committed locally so
the chooser does not depend on third-party favicon services or runtime image
hotlinks; provenance is recorded beside the assets.

## Deploy To Production

The checked-in nginx deployment serves the generated site from a read-only
mount and runs with a read-only root filesystem, bounded temporary filesystems,
health checks, log rotation, `no-new-privileges`, and conservative browser
security headers. It expects the external `stack-network` used by the TLS
reverse proxy.

```bash
cp deploy/.env.example deploy/.env
# Set CELLSCRIPT_SITE_DIST to the absolute generated-site directory.
docker compose --env-file deploy/.env -f deploy/docker-compose.production.yml config
docker compose --env-file deploy/.env -f deploy/docker-compose.production.yml up -d
```

TLS terminates at the shared reverse proxy. The origin still emits HSTS and
anti-embedding/security headers so the HTTPS response retains them through the
proxy.

## Registry Data

The site includes generated registry metadata at:
Expand All @@ -54,7 +90,14 @@ The site includes generated registry metadata at:
src/data/registry-packages.json
```

When this repository is checked out as a submodule inside the main CellScript repository, the generator can scan the parent checkout for package metadata. When this repository is used standalone and no package sources are present, the generator keeps the committed registry data instead of erasing it.
When this repository is checked out as a submodule inside the main CellScript
repository, the generator scans the parent checkout for package metadata. It
fails closed when no `registry.json` source exists, when the source has no git
revision, or when its origin is not a credential-free HTTPS URL; it never
silently retains an older generated file. Build-time CellScript packages are
classified as `profile_library` when `Cell.toml` declares `metadata.profile`,
otherwise as `source_library`. Source links are pinned to the package
repository revision rather than a moving branch.

Manual regeneration:

Expand Down
2 changes: 2 additions & 0 deletions deploy/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Absolute path to the generated Astro dist directory on the deployment host.
# CELLSCRIPT_SITE_DIST=/data/cellscript/site
37 changes: 37 additions & 0 deletions deploy/docker-compose.production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: cellscript

services:
site:
image: nginx:1.27-alpine
container_name: cellscript-site
restart: unless-stopped
environment:
VIRTUAL_HOST: cellscript.dev,www.cellscript.dev
VIRTUAL_PORT: "80"
volumes:
- ${CELLSCRIPT_SITE_DIST:-../dist}:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- stack-network
read_only: true
tmpfs:
- /var/cache/nginx:size=16m
- /var/run:size=1m
- /tmp:size=4m
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/"]
interval: 15s
timeout: 5s
retries: 5
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

networks:
stack-network:
external: true
name: stack-network
30 changes: 30 additions & 0 deletions deploy/docker-compose.testnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: cellscript-testnet-site

services:
site:
image: nginx:1.27-alpine
container_name: cellscript-testnet-site
restart: unless-stopped
environment:
VIRTUAL_HOST: ${CELLSCRIPT_TESTNET_SITE_HOST:-testnet.registry.cellscript.dev}
VIRTUAL_PORT: "80"
volumes:
- ${CELLSCRIPT_TESTNET_SITE_DIST:?CELLSCRIPT_TESTNET_SITE_DIST is required}:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
networks: [stack-network]
read_only: true
tmpfs:
- /var/cache/nginx:size=16m
- /var/run:size=1m
- /tmp:size=4m
security_opt: [no-new-privileges:true]
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/registry"]
interval: 15s
timeout: 5s
retries: 5

networks:
stack-network:
external: true
name: stack-network
82 changes: 82 additions & 0 deletions deploy/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;

server_tokens off;
absolute_redirect off;

gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types application/wasm;

add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;

location = /docs/tutorial-09-action-model-and-0-13-syntax {
return 301 https://cellscript.dev/docs/tutorial-09-action-model-and-canonical-syntax/;
}

location = /docs/tutorial-09-action-model-and-0-13-syntax/ {
return 301 https://cellscript.dev/docs/tutorial-09-action-model-and-canonical-syntax/;
}

location = /docs/tutorial-13-agentic-loops-and-cellc-mcp {
return 301 https://cellscript.dev/docs/tutorial-13-agentic-loops-and-cellscript-mcp/;
}

location = /docs/tutorial-13-agentic-loops-and-cellc-mcp/ {
return 301 https://cellscript.dev/docs/tutorial-13-agentic-loops-and-cellscript-mcp/;
}

location = /registry/interface {
return 301 https://cellscript.dev/registry/LS-IDL;
}

location = /registry/interface/ {
return 301 https://cellscript.dev/registry/LS-IDL;
}

location = /registry/LS-IDL {
try_files /registry/LS-IDL/index.html =404;
}

location = /registry/LS-IDL/ {
return 301 https://cellscript.dev/registry/LS-IDL;
}

location / {
try_files $uri $uri/ /index.html;
}

location ~* \.wasm$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable" always;
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
try_files $uri =404;
}

location ~* \.(?:css|js|png|jpg|jpeg|gif|webp|svg|ico|woff2?)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800" always;
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
try_files $uri =404;
}
}
23 changes: 13 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 21 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,42 @@
"version": "0.1.0",
"private": true,
"type": "module",
"engines": {
"node": ">=22 <23"
},
"scripts": {
"prepare:registry": "node scripts/generate-registry-data.mjs",
"data:github": "node scripts/fetch-github-data.mjs",
"data:regen": "node scripts/regen-website-data.mjs",
"dev": "npm run prepare:registry && astro dev --host 127.0.0.1",
"build": "npm run prepare:registry && astro check && astro build && npm run check:docs && npm run check:dist",
"build": "npm run prepare:registry && npm run build:ci",
"build:ci": "npm run test:registry-guidance && npm run test:registry-browse && npm run test:registry-visual-language && npm run test:session-storage && npm run test:submit-draft && npm run test:playground-focus && npm run test:playground-session && npm run test:playground-presentation && npm run check:visual && astro check && astro build && npm run check:homepage && npm run check:ls-idl && npm run test:site-preferences && PUBLIC_REGISTRY_ENVIRONMENT=testnet-sandbox astro build --outDir dist-testnet && node scripts/test-site-preferences.mjs dist-testnet && node scripts/check-ls-idl-regressions.mjs dist-testnet && node scripts/check-testnet-build.mjs && npm run check:registry-parity && npm run check:docs && npm run check:dist && npm run check:deploy",
"build:testnet": "npm run prepare:registry && npm run test:registry-guidance && npm run test:registry-browse && npm run test:registry-visual-language && npm run test:session-storage && npm run test:submit-draft && npm run test:playground-focus && npm run test:playground-session && npm run test:playground-presentation && npm run check:visual && PUBLIC_REGISTRY_ENVIRONMENT=testnet-sandbox astro check && PUBLIC_REGISTRY_ENVIRONMENT=testnet-sandbox astro build --outDir dist-testnet && node scripts/test-site-preferences.mjs dist-testnet && node scripts/check-ls-idl-regressions.mjs dist-testnet && node scripts/check-testnet-build.mjs",
"test:registry-guidance": "node scripts/test-registry-guidance.mjs",
"test:registry-browse": "node scripts/test-registry-browse-state.mjs",
"test:registry-visual-language": "node scripts/test-registry-visual-language.mjs",
"test:session-storage": "node scripts/test-authorisation-session-storage.mjs",
"test:submit-draft": "node scripts/test-registry-submit-draft.mjs",
"test:playground-focus": "node scripts/test-playground-focus.mjs",
"test:playground-session": "node scripts/test-playground-session.mjs",
"test:playground-presentation": "node scripts/test-playground-presentation.mjs",
"test:site-preferences": "node scripts/test-site-preferences.mjs",
"check:visual": "node scripts/check-visual-contract.mjs",
"check:homepage": "node scripts/check-homepage-regressions.mjs",
"check:ls-idl": "node scripts/check-ls-idl-regressions.mjs",
"check:registry-parity": "node scripts/check-registry-environment-parity.mjs",
"check:docs": "node scripts/check-doc-links.mjs",
"check:dist": "node scripts/check-dist-regressions.mjs",
"check:deploy": "node scripts/check-production-deploy.mjs",
"preview": "astro preview --host 127.0.0.1"
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@ckb-ccc/connector": "^1.1.0",
"@phosphor-icons/core": "^2.1.1",
"astro": "^5.9.2",
"buffer": "^6.0.3",
"fflate": "^0.8.3",
"lucide-astro": "^0.556.0",
"marked": "^18.0.5",
"mermaid": "^11.15.0",
"smol-toml": "^1.6.0",
Expand Down
47 changes: 41 additions & 6 deletions public/playground-worker.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,41 @@
let wasmModulePromise;
let wasmModule;
const COMPILER_ASSET_VERSION = "20260731-v0.22.0-9bb2d765";
const COMPILER_ASSET_VERSION = "20260811-v0.23.0-fa369818";
const CELLSCRIPT_EDITION = "2026";
const COMPILER_LOAD_TIMEOUT_MS = 12_000;

const loadCompiler = async () => {
if (!wasmModulePromise) {
wasmModulePromise = import(`/wasm/cellscript_wasm.js?v=${COMPILER_ASSET_VERSION}`).then(async (mod) => {
await mod.default({ module_or_path: `/wasm/cellscript_wasm_bg.wasm?v=${COMPILER_ASSET_VERSION}` });
wasmModule = mod;
return mod;
});
wasmModulePromise = (async () => {
const controller = new AbortController();
let timedOut = false;
const timeout = setTimeout(() => {
timedOut = true;
controller.abort();
}, COMPILER_LOAD_TIMEOUT_MS);

try {
const mod = await import(`/wasm/cellscript_wasm.js?v=${COMPILER_ASSET_VERSION}`);
const response = await fetch(`/wasm/cellscript_wasm_bg.wasm?v=${COMPILER_ASSET_VERSION}`, {
cache: "force-cache",
signal: controller.signal,
});
if (!response.ok) throw new Error(`compiler download failed with HTTP ${response.status}`);
await mod.default({ module_or_path: response });
wasmModule = mod;
return mod;
} catch (error) {
wasmModulePromise = undefined;
if (timedOut) {
const timeoutError = new Error("compiler download timed out");
timeoutError.code = "compiler_load_timeout";
throw timeoutError;
}
throw error;
} finally {
clearTimeout(timeout);
}
})();
}
return wasmModulePromise;
};
Expand Down Expand Up @@ -61,6 +87,15 @@ self.addEventListener("message", async (event) => {
payload,
});
} catch (error) {
if (!wasmModule) {
self.postMessage({
id,
type: "compiler-error",
code: error?.code || "compiler_load_failed",
message: error instanceof Error ? error.message : String(error),
});
return;
}
self.postMessage({
id,
type: "result",
Expand Down
Loading
Loading