Skip to content

feat(web) Private space - #274

Open
cuongdcdev wants to merge 124 commits into
mainfrom
feat/kent/web
Open

feat(web) Private space#274
cuongdcdev wants to merge 124 commits into
mainfrom
feat/kent/web

Conversation

@cuongdcdev

@cuongdcdev cuongdcdev commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Private Space

Adds the signed-in half of the hub: a place to submit, store, review and
publish your own skills and tools.

  • Workspace at /dashboard — catalog, item page, edit, members,
    settings, one header control cluster across routes.
  • Private artifacts — upload a skill or tool as a single zip, validated
    before ingest; content lives in S3-compatible storage; owners can browse
    and download what is stored.
  • Publish lifecycle — draft → checks → publish, with real checks instead
    of placeholders; published items install through IronClaw.
  • Orgs & teams — multi-org membership on the BetterAuth organization
    plugin, invitations by email or NEAR account id.
  • Local dev stack — single-image Postgres 16 + SeaweedFS so the storage
    path runs offline.

Tools

feat(tools): sync 15 reborn WASM tools with manifest v3 and bumped patch versions

  • Update 15 verified WASM tools (tools/) with extension manifest v3 schemas.
  • Include mandatory origin_gate_matrix policies required for IronClaw 1.1+.

NOTE mandatory origin_gate_matrix tag is required for Ironclaw 1.1+ BUT this will cause tool installation on Ironclaw 1.0 to fail.
Already created a report here nearai/ironclaw#7320 and a proposa, Firat is working on a fix

Type

  • Use case
  • New tool
  • New skill
  • Bug fix
  • Documentation / process
  • Infrastructure

Artifact checklist

Tool PRs:

  • Adds or updates tools/<tool-name>/README.md
  • Adds input/output schema assets that exactly match the generated manifest refs
  • Documents auth, scopes, limits, and target use cases
  • Tests representative action calls and records the results below

Docs/process PRs:

  • No tracking.md or README shipped-catalog change is needed
  • Or, shipped catalog changes are reflected in both tracking.md and README

cuongdcdev and others added 16 commits July 12, 2026 10:56
- DefiLlama: Programmatic access to DeFi metrics (TVL, protocols, chains, yields)
- Tavily: A search engine built for LLMs/agents to run real-time structured web queries
…skills

- tavily_search_skills
- trading_skill_v2
- woo_copilot
- woo_product_copywriter
- wp_content_research_writer
- wp_copy_editor
- wp_publisher
- wp_seo_auditor
- wp_social_sentiment_miner
WASM Tools:
- coingecko: Universal cryptocurrency price and market oracle. Retrieves current prices, market tables, detailed metadata, historical charts, candles, and trending assets.
- etherscan: EVM explorer across 60+ EVM-compatible networks. Retrieves wallet balances, transaction histories, and ERC-20/721/1155 token transfers.
- jina: Web reader (URLs to markdown), screenshot capturer, and web/academic/image search utility.
- pikespeak: NEAR Protocol indexer and wealth portfolio tracker (native/token balances, validator APY, and DeFi positions like Rhea).

Agent Skills:
- brand_reputation_monitor: Monitors brand and keyword mentions on major social platforms (X, Reddit, TikTok, Facebook, Threads), runs sentiment analysis, and prepares response drafts.
- crypto_trend_sentiment: Cross-references CoinGecko trending crypto assets with discussions and hype on major social platforms (X, Reddit, TikTok, Threads) to gauge market sentiment.
- local_business_lead_generator: Harvests local business leads from Google Maps, scrapes websites for contact emails, and formats prospect lists.
- smart_buyer_assistant: Scouts e-commerce platforms (Google Shopping, Amazon, eBay, etc.) for specs, reviews, and price drops.

Other:
- defillama: Update README.md with a screenshot link.
…youtube) and YouTube skills

Add 4 new WASM tools and 3 YouTube domain skills:

WASM Tools:
- frankfurter-fx: Foreign exchange (FX) rates, batch conversions, and historical rate analytics via Central Bank open data.
- messari: Crypto market assets, historical metrics, market intelligence, and crypto news feed.
- serper: High-speed Google search API integration (organic search, news, images, and places).
- youtube: YouTube Data API v3 integration (video metadata, channel stats, playlist items, and video transcripts/captions).

Skills:
- youtube_recap_expert: Video transcript summarization, key takeaway extraction, and structured video recaps.
- youtube_content_insights: In-depth YouTube channel performance analytics, video metrics auditing, and engagement insights.
- youtube_competitor_analyst: Competitive benchmarking, content gap analysis, and video strategy advisor for YouTube creators.
…ing:

- bluesky-analytics, coingecko, crypto-ta-engine, defillama, etherscan
- firecrawl, frankfurter-fx, jina, messari, nearcatalog
- pikespeak, serper, tavily, wordpress, and youtube
IronHub previously assumed every tool manifest had to be reconstructed from legacy capabilities JSON and forced that translation through the Reborn v3 generator. Native Reborn packages already carry their authoritative manifest, including per-method schemas, network targets, credential audiences, and runtime metadata. Regenerating those packages as v3 could collapse their method surface into a generic invoke tool and reject valid v2 credential layouts even though IronClaw supports both manifest versions.

Validate checked-in native v2/v3 manifests against the tool directory and Cargo metadata, verify every referenced schema can be packaged, and publish the native manifest unchanged. Keep capabilities-to-v3 generation only as a compatibility path for legacy tools that do not yet ship manifest.toml, and limit translation regression tests to that legacy set.
Add native Reborn v2 manifests, first-class method schemas and prompts, updated WASM sources, and catalog version bumps for the manually tested tool set.

Updated: bluesky-analytics, coingecko, crypto-ta-engine, defillama, etherscan, firecrawl, frankfurter-fx, jina, messari, nearcatalog, pikespeak, serper, tavily, wordpress, and youtube.
Resolve warnings promoted to errors by the IronHub CI matrix for both wasm32-wasip2 release builds and native test builds. Apply idiomatic formatting, prefix handling, conditional compilation, and narrowly scoped argument-count allowances without changing tool behavior.
Marketplace detail pages previously resolved repository README images against
the browser route, exposed misleading action and trigger totals, and rendered
credentialed tools as unauthenticated when their security declarations lived
outside the narrow legacy shape understood by the catalog.

Serve repository-relative images through a constrained image-only asset route,
and normalize authentication, credential audiences, network targets, effects,
and permissions from both legacy capabilities JSON and native Reborn manifests.
Keep true credential-free tools explicit while presenting Bearer, API key,
Basic, query parameter, and OAuth with PKCE models in user-facing language.

Remove action and trigger counters, action-based sorting, inferred limits, and
the confusing create-skill action. Reorganize technical details around access,
network permissions, implementation metadata, activation keywords, and source
resources, while promoting Install to Agent in the mobile detail header.

Update installation examples to the ironclaw ironhub command namespace and add
an accessible, reduced-motion-aware scroll-to-top control outside the agents
workspace. Cover asset confinement, manifest normalization, and catalog sorting
fallbacks with focused regression tests.
Make IronHub's public marketplace, collections, and use cases easier to find,
preview, cite, and consume across search engines, social platforms, and AI
agents.

Generate descriptive page metadata, canonical URLs, branded social cards, and
schema.org structured data from public catalog records. Publish crawler
controls and a resilient sitemap while preventing authenticated and
partner-only pages from being indexed.

Support agent-readable discovery through llms.txt, Markdown indexes, and
appendable .md URLs such as /usecases/<id>.md. Generate these representations
directly from the existing source records, return canonical response headers,
and advertise them as typed alternatives from the corresponding HTML pages.
Co-authored-by: cuongdcdev <12132077+cuongdcdev@users.noreply.github.com>
# Conflicts:
#	scripts/check-extension-manifests.sh
#	tools/crypto-ta-engine/Cargo.lock
#	tools/nearcatalog/Cargo.lock
#	web/features/marketplace/components/marketplace-screen.tsx
… patch versions

- Update 15 verified WASM tools () with extension manifest v3 schemas.
- Include mandatory  policies required for IronClaw 1.1+.
@cuongdcdev
cuongdcdev requested a review from neo-sky as a code owner August 7, 2026 10:02
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ironhub-web Error Error Sep 4, 2026 5:22pm UTC

Request Review

@cuongdcdev cuongdcdev changed the title Feat/kent/web feat(tools): sync 15 reborn WASM tools with manifest v3 and bumped patch versions Aug 7, 2026
Co-authored-by: cuongdcdev <12132077+cuongdcdev@users.noreply.github.com>
neo-sky and others added 6 commits August 10, 2026 12:29
…, and rate limiting

Implements task group 2 (wt-artifact-api) of add-private-workspace:
- PATCH /api/private-artifacts/[id]: partial update of title/description/
  visibility/sourceUrl; unknown or immutable fields (name/version/type) -> 400.
- DELETE /api/private-artifacts/[id]: cascades content rows via Prisma, then
  best-effort S3 prefix cleanup (log-only on failure).
- DELETE /api/private-artifacts/[id]/content/[kind]: removes one content row
  + its S3 object, added additively next to the existing PUT handler.
- POST /api/private-artifacts/[id]/token: mints a 1-hour manifest-scope
  token via the existing mintArtifactToken and returns an absolute
  manifest URL.
- New reusable fixed-window rate limiter (web/lib/http/rate-limit.ts,
  swappable store), applied to the two public token routes (manifest and
  content-by-token) with 429 + Retry-After.
- Stub web/lib/storage/index.ts (deleteObject/deleteByPrefix + other S3 lib
  signatures) so this worktree typechecks ahead of wt-storage's real
  implementation; marked for integration replacement.

All mutation routes stay session + requireActiveOrganization() + org-scoped
(404 on cross-org access) + assertSameOriginRequest guarded.
…storage

Add web/lib/storage/ (put/get/delete/deleteByPrefix/presigned-URL) backed by
@aws-sdk/client-s3, configured from S3_* env vars (S3_PUBLIC_ENDPOINT for
presigned URLs). Replace PrivateArtifactContent.bytes with storageKey in the
schema/migration, rewrite content.ts to upload to S3 under
private-artifacts/{orgId}/{artifactId}/{kind} and compute sha256 server-side,
and switch the token download route to a 302 redirect to a <=5min presigned
URL (Cache-Control: no-store) instead of proxying bytes.

Adds a storage smoke script (pnpm storage:smoke) for the local dev stack and
unit/route tests (pnpm test:storage) that mock the S3 SDK and Prisma via
node:test module mocking, plus a small ESM loader so plain `node --test` can
resolve this repo's "@/..." tsconfig path alias.
…ons API

Add web/app/api/orgs routes for listing/creating/renaming orgs, switching the
active org, leaving/removing/re-roling members with owner/admin/member
permission checks and last-owner protection, and full invitation lifecycle
(create, list pending-by-email, list org invitations, accept/reject/cancel)
with 7-day expiry and no email sending. Business logic lives in
lib/orgs/{roles,service,invitations}.ts with unit tests covering the
permission matrix, duplicate/member 409s, expiry filtering, and last-owner
protection.
Managing an item and editing it are one job, but they lived on two routes:
"Add instructions file" in Files had to link away and back, and every change
cost a round trip. The edit forms move into features/partner/components as
SkillEditor and ToolEditor, the item page renders one of them inline, and the
old routes redirect so links already handed out still land somewhere useful.
Copy install link, Download and Publish move up beside the title.

Fixes a data-loss bug on the way: the skill form seeded `description` from
the stored file's frontmatter alone, while the catalog reads it off the
artifact record. An item whose description lived only on the record opened
with a blank field, and saving wrote that blank straight back over the
catalog copy. Frontmatter still wins when it has one; otherwise the record
does.

Drops *.capabilities.json from the workspace UI. manifest.toml (schema
reborn.extension_manifest.v3) carries the effects, default permission and
secrets handles it used to, so presenting it as a file the owner edits made
two sources of truth out of one — and the editor made it `required`, which
silently blocked every save for a tool that shipped without one. Ingest,
checks and the signed manifest are untouched, so nothing changes for an
agent. In its place the Files section lists the schema and prompt files the
package declares, by the path the agent will ask for.

Each stored file gets a Download button, "Download instructions" reads
"Download skill", and the instructions section names the file it edits.
Four selects side by side did not fit the workspace's content width: at 1440
they measured 658px against 888px of room, so they took a row of their own
and pushed the view toggle onto a third. They also drew the eye every time
whether or not anything was filtered.

A new FilterMenu primitive holds them behind one button with a count of how
many are set, which is the only thing worth seeing at a glance. Search,
Filters and the view toggle now share one row, and search keeps 546px of it.
Each select gained a visible label inside the panel, so the "Type: Skills"
prefixes that stood in for one are gone.
An owner could see that a .zip was stored for their tool, and its size,
and nothing else about it. The files that archive actually contains --
the thing they are being asked to trust and replace -- were only ever
visible to ingest.

`listBundleEntries` exposes what `listZipEntries` already parsed: the
central directory alone, so no entry is inflated and a 25MB archive
stays cheap to read. Directory records are dropped (a zip may or may not
carry them, and the folder tree is derivable from the paths either way)
and so are symlinks, which `inspectExtensionBundle` rejects outright.

The listing is computed per request rather than recorded at upload time.
It is a property of the bytes in storage, and deriving it there means it
cannot drift from them the way a persisted copy could.
A tool is created from a single .zip and every file the hub stores for
it is extracted from that archive, but the workspace then presented
those files as three separately managed things -- Program file, Setup
details, Uploaded package -- each with its own card and its own upload.
Replacing one of them alone was the worst case: swapping the .wasm left
manifest.toml describing bytes that were no longer stored.

So the whole package is now the unit of change. The tool editor's step 3
is Package: what is stored, what is inside it, and one drop target that
replaces all of it. The .wasm-only upload is gone. The generic Files
section is a skill's single instructions file now and nothing else, and
the per-file downloads went with it -- the header still offers the whole
package.

What is inside is a plain file tree built from the archive's own paths,
folders first, sizes on the right. Not the manifest's declared assets:
the reader is asking what they uploaded, not what the installer will
match. Long listings state how many files they left out rather than
truncating silently.

Uploading runs immediately rather than on Save changes. The PUT rewrites
every stored content row and the entire declared asset set server-side,
which is not something to fold into a form submit that could half-apply
-- the drop target says so.

Also puts the item's type first in the header badges, and moves the
"add the required files" reason out of the Files section that a tool no
longer renders, so the disabled Copy install link keeps its description.
…w panel

"Capabilities file is valid JSON" warned every tool that stores no
*.capabilities.json, which is now the normal case and, since the file
disappeared from the workspace, one the owner cannot act on: nothing
offers to add or fix a document they never see. A row on the panel that
exists to say why publishing is blocked has to be actionable.

Nothing is lost by removing it. *.capabilities.json is the legacy
carrier of data reborn.extension_manifest.v3 moved into manifest.toml,
and the agent files whatever we publish under `legacy/capabilities.json`
and never reads it (ironclaw-contract.ts). The one bound that still
matters -- its size -- is checked by agent_contract via verification.ts's
`capabilities_size`, which is untouched.
`core` is a 569KB crash dump that was committed by accident. Removed,
and `core` / `core.*` added to .gitignore so the next one stays out.
"MVP" was a note to ourselves about how finished the thing was, sitting
in the address bar of the product members use every day. The workspace
now lives at /dashboard, and its list screen -- previously the doubled
/mvp/dashboard -- is /dashboard/catalog, which says what the page shows.

Every old address still resolves: next.config redirects /mvp,
/mvp/dashboard and /mvp/:path* permanently, with the /mvp/dashboard rule
ahead of the wildcard so it does not land on /dashboard/dashboard. Links
already shared or bookmarked keep working.

The env var behind the route gate keeps its NEXT_PUBLIC_DISABLE_MVP_ROUTE
name even though the flag reading it is now isWorkspaceRouteDisabled: it
is already set in deployed environments, and renaming it would silently
un-hide the workspace wherever the new name is not set yet.

Route checks that were spelled as `pathname.startsWith("/mvp")` in three
places are one `isWorkspaceRoute` helper, and the sessionStorage key for
the catalog's view preference follows the rename.

Labels and toasts on the moved screens are aligned with the wording the
rest of the workspace already uses ("Back to your catalog", "<name>
created"), rather than leaving each screen with its own phrasing.
The header offered a different set of controls depending on where you
were: an account button on marketing pages, a notification bell only
inside the workspace, a Docs button competing with the workspace's own
Developer Docs link. A signed-in member now gets the same cluster
everywhere -- bell, theme, account menu -- grouped into one capsule,
with the account button replaced by a real menu (UserMenu) instead of a
link.

UserMenu reads useSearchParams, so it sits behind a Suspense boundary:
without one it would force the root layout, and therefore every route,
out of static rendering.

ToastProvider moves to the root layout. It used to be mounted twice --
once around the header's bell, once around the workspace shell -- which
gave the two halves of the same screen separate feedback stacks. One
provider, one stack.

The workspace sidebar drops the support card and the mobile sheet it
carried; navigation on small screens is the site's own mobile nav, and
the sidebar no longer duplicates it.
The agent install form minted a fresh shared key on every visit and
every tab switch, so anyone who looked away from the page had to export
the variable and restart their agent again to match it. A key this
browser generated is now remembered and offered back.

Only generated keys are stored. A pasted key already lives on the user's
agent, and copying it into this browser would spread the secret for
nothing. What is stored is still a shared secret in localStorage,
readable by any script on this origin -- that is the trade, and the
alternative was a key that could not survive a reload. Anything in
storage that could not have come from the generator is ignored rather
than shown back as if the hub had issued it.

Also stops truncating account names: a 64-character implicit NEAR
account id has no natural break points, so it wraps rather than
ellipsising into something the owner cannot read.
Tool identifier and version now come from the name and the uploaded
package, as a skill's already do, so neither is typed. A name another
item holds gets a -2 suffix server-side instead of a 409 about a field
that is no longer on screen.

The minted install link renders in full on the manage page: a clipboard
write rejects whenever the document is not focused, and that left the
author with nothing.
Sub-accounts, .tg, testnet and implicit accounts are valid invitees:
account ids are matched against NEAR's own grammar, and the address
better-near-auth stores for them is no longer re-validated as an email,
which is what rejected work.efiz.near as "Invalid email address".

The members page lists invitations through the org route instead of
the BetterAuth client, so each row can show the NEAR account id it was
addressed to rather than a temp-<hex>@ placeholder.

Also: TRUSTED_ORIGINS is read into the auth config, and dev requests
from *.nearcatalog.xyz are allowed.
@cuongdcdev cuongdcdev changed the title feat(tools): sync 15 reborn WASM tools with manifest v3 and bumped patch versions FEAT(private space)! Aug 24, 2026
@cuongdcdev cuongdcdev changed the title FEAT(private space)! feat(web) Private space Aug 24, 2026
- Add icons to sidebar sub-navigation (Skills, Tools, Souls)
- Add icons to catalog KPI stat cards (Total items, Skills, Tools, Souls, Drafts)
- Optimize KPI row for mobile with smooth horizontal scroll
- Inline search and filter toolbar on mobile
- Rename 'Your catalog' to 'Catalog' for consistency with workspace navigation
A private artifact was unreachable whenever a public entry shared its
slug: resolution tried public first and returned it, so the install
handshake signed the wrong artifact. Dispatch on the requested source
instead, and let a private miss answer 404 rather than falling through.

Membership stays inside the query so a non-member and an absent name are
answered identically; only a member learns the organization's name.
Version was absent from the mutable field set, so an author could never
bump it and the intended update model — publish a new version, install
replaces the old — was unreachable.

Making it mutable alone would let a published version's bytes change
under installers who already verified its digest. A new publishedVersion
column records the version the current files were published as, and
content and asset writes are refused while it matches. Bump the version
and the artifact reopens for editing.

Version must move strictly forward, build metadata excluded from
precedence, so 1.0.0+a to 1.0.0+b is refused rather than silently
accepted as a new version naming different bytes.
A soul is one SOUL.md, optionally with a README. The README is hub-only:
it never enters the manifest and never enters the digest, and the
agent-facing content route refuses it before verifying the token, so a
valid install token cannot reach it by guessing the URL.

Souls follow the same visibility rules as every other type. A public one
is read as the opening of an installer's system prompt, ahead of their
memory and tools, so install shows the full text first — regardless of
visibility, and enforced by the prop union rather than by convention.

Digest is skill_artifact_digest's no-files branch over the SOUL.md hash.
A loadout holds tools, skills and at most one soul, private or public,
one level deep. Private items come from hub storage; public ones resolve
live from the release and the Iliad backend rather than being copied, so
upstream revocation still reaches an installer.

Each item's digest is pinned at publish. A private item that drifts
blocks the install; a public one that moves upstream is reported to the
owner but does not block, because no source retains the bytes that were
pinned. Publishing re-pins every item, so republishing is the repair.

An upstream release change marks affected loadouts for re-verification,
which runs lazily on read and unconditionally at install. Install is
all-or-nothing and names every failing item.

Tokens carry a loadout scope authorized against membership per request,
and rate limits are keyed per item so a multi-item install is not
throttled partway through. The publish gate measures the assembled
manifest against the agent's ceiling instead of capping item count.

Install itself is presented as unavailable, never mocked: it needs a
multi-entry payload the agent does not have yet.

Also fixes a duplicate Publish button and a duplicate DOM id on the
manage page, where the responsive layout rendered the primary action
twice and aria-describedby resolved to the hidden copy.
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.

3 participants