feat(docs): getting started for agents - #29
Conversation
getServiceSignInMessage is no longer deterministic: the message embeds a server-issued single-use nonce and its expiry, so a signature over it can neither be replayed nor cached. Verified against staging. - authentication.md dropped the "deterministic message" claim and now states the single-use rule, the 10-minute window and last-write-wins per (wallet, serviceName). - service-tokens.md selects the new `expiresAt`, documents the field, and carries the failure table keyed on details.reason: NONCE_NOT_FOUND (never requested, or already consumed), NONCE_EXPIRED, INVALID_SIGNATURE (altered text, or superseded by a later fetch) and WALLET_MISMATCH. Superseded returns INVALID_SIGNATURE, not NONCE_NOT_FOUND -- the record still exists, it just holds a newer nonce. - changelog.md announces the change with a migration note, and fixes an IPNFT agreements snippet that omitted the required `id` argument. Confirmed on staging: window measured at 10.02 min, replay rejected, supersession rejected, re-worded message rejected, expiry rejected (9/9 checks). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tutorial 1 failed outright on 1 of 5 runs against staging:
initiateCreateOrUpdateFile returned NOT_FOUND ("Project 0x... does not
exist") immediately after createLab had returned success.
Cause is an asymmetry, not a transient: onchain_lab rows are written by the
event indexer that ingests the mint, and createLab tolerates their absence
via its ownerOf fallback (auth-service.ts:615) while the content writes read
the indexed record directly (auth-service.ts:283-300). So createLab can
succeed on a lab that is not yet writable.
- withIndexerLagRetry now lives in the shared setup block, with both lag
cases named: NOT_FOUND after a mint, UNAUTHORIZED after a role grant.
- Tutorial 1 Step 4 wraps its first call in it, in the snippet and the
complete script, and warns that Step 3 succeeding is no guarantee.
- Tutorial 3's withRoleGrantRetry is renamed to the same helper with
codes: ["UNAUTHORIZED", "NOT_FOUND"], so both tutorials teach one idiom.
- for-agents.md gains the rule as #8.
- Both upload tutorials document `UPSTREAM_UNAVAILABLE: "Path is occupied"`
as NOT retryable despite the code -- a re-run against the same lab. Fix
is a new `path`, or `ref` to add a version.
- Tutorial 1/3 Step 1 also carry the nonce rules from the previous commit,
since they share those regions.
- Tutorial 3 no longer implies isAgent must be true; a grant with
isAgent=false authorized an upload on staging exactly the same.
Verified: helper unit-tested as published (5/5), Tutorial 1 three clean
runs, full T1->T2->T4 chain on lab-1542, Tutorial 3 clean on testing-lab.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…asks Follow-ups from the IP-3028 audit, each verified against staging. - browse-and-search.md: three public-endpoint callouts claimed the consumer credential goes in as `Authorization: Bearer`. It does not -- the same error already fixed in lab-management.md. - tokenization-api.md: the 401 row said to check `Authorization: Bearer`, contradicting the same page's own header section. Its two "contact the Molecule team" asks now point at the published Contracts reference for ABIs/addresses and at the single credential-request template for the rest. - ipnft-api-deprecated.md: the four-step credential ask collapses to that same template. - files.md: updateFileMetadata's parameter table omitted the required accessLevel, and `ref` now says explicitly that it is the datasetId, not the file path. - docs-sync.md: restore the api-reference/authentication.md row, dropped when the getting-started rows were added, and point service-tokens.md at the sign-in nonce constants and reason codes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two decisions from the IP-3028 review. 1. `error.details` is doubly encoded in-band today (toGraphqlApiError stringifies into an AWSJSON field and AppSync serializes it again), so the documented single `JSON.parse(...).reason` returned `undefined` silently and any agent branching on `reason` took the wrong path. Rather than wait on the backend fix, the docs now teach a `parseDetails` helper that loops until the value is no longer a string: correct for a plain object (thrown queries), a single-encoded string, and today's double-encoded string alike, so it needs no change once the encoding is corrected. Applied to the shared setup block, all four tutorial scripts' assertOk, the Labs API error-handling reference, the agent one-pager, the changelog migration snippet, lab-management and legal-agreements. Verified 9/9 including against a live staging error. 2. Deleted `api-reference/IPNFT-api.md` — an orphan duplicate of `ipnft-api-deprecated.md`, never in SUMMARY.md, still teaching the retired `x-api-key` header, and holder of the last "contact the Molecule team" in api-reference/. Nothing linked to it. docs-sync.md records the removal. Also widens the indexer-lag retry budget on measured evidence: the earlier 5 attempts / ~30s was sized on a lag of seconds, but a mint on staging took over four minutes to index (probed: NOT_FOUND for 211s straight, writable later). The helper is now 12 attempts with backoff capped at 30s (~4 min) and logs each wait, and the prose no longer claims it "clears in seconds". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Requested by Logan, for the PeptAI/Martin shape: an owner wallet, an agent wallet and the Lab's own OCL account are all in play at once, each documented in a different place and never side by side. A reader assembling the picture had to visit onchain-lab.md, roles-and-permissions.md, authentication.md and Tutorial 3. Adds one table to authentication.md — what each wallet is, who holds the key, how it gets its rights, how it authenticates, **which field its address goes in**, and what it cannot do — plus the two failure modes it prevents: - Passing the owner's address where `labAccountAddress` belongs in accessControlConditions. Evaluation fails closed, so the upload succeeds and then nobody can decrypt, with no error explaining why. - Expecting the agent to inherit the human's reach; its rights come from its own grant, which is why a few mutations stay Owner-only. Also documents that `oclId` is not a wallet address: it packs the version, namespace, LabNFT tokenId and the OCL account address, so its trailing 40 hex chars are `labAccountAddress`. Layout taken from `lambda/common/utils/ocl-id.ts` and verified by decoding three live staging labs (testing-lab/1526, lab-1533, lab-1546) — each decoded tokenId and address matches the values the API returns. That is also why a zeroed oclId is rejected with "embedded address is zero" rather than a not-found. Linked from Tutorial 3, Tutorial 2's conditions step, the agent one-pager (prose plus a new rule 9) and roles-and-permissions.md, so the scattered partial explanations become pointers to one canonical table. Not asserted: whether PeptAI's owner wallet is specifically a Privy embedded wallet. The table says "typically a Privy embedded wallet created at email sign-in, but any wallet that holds the LabNFT" — worth confirming with Martin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| LAB_PAGE https://testnet.labs.molecule.xyz/projects/<shortname> | ||
| ``` | ||
|
|
||
| Production: `https://production.graphql.api.molecule.xyz/graphql`, `base` (8453), factory `0xECdF4f05384056507485C90aeAb0a83268760D6E`, LabNFT `0x9F96027eeAFb9ad5F2b5d7043B36Ee96B2EeBE92`, AccessResolver `0x89a14Be8f7824d4775053Edad0f2fA2d6767b72B`, condition chain `"base"`, lab page `https://labs.molecule.xyz/projects/<shortname>`. |
There was a problem hiding this comment.
I think we could format this also as a code block, similar to staging.
There was a problem hiding this comment.
As announcements are somewhat deprecated or at least not shown anymore, I guess we should not highlight it anymore.
| ## Optional — announce it | ||
|
|
||
| ```graphql | ||
| mutation CreateAnnouncement($oclId: String!, $headline: String!, $body: String!, $attachments: [String!]) { | ||
| createAnnouncement(oclId: $oclId, headline: $headline, body: $body, attachments: $attachments) { | ||
| message | ||
| error { code message requestId retryable details } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| `attachments` takes the `datasetId` values returned by `finishCreateOrUpdateFile`. |
There was a problem hiding this comment.
same here, I think we shouldn't mention announcements any longer.
There was a problem hiding this comment.
I guess we could just remove this page to keep our docs slim. It was published recently as a quick explanation for an agentic presentation by the product team and shouldn't have a big audience.
Announcements are no longer surfaced in the Molecule app, so they are out of every onboarding, how-to and feature-description surface: - delete the Announce tutorial and the "Create Announcement" section in labs-api/files.md - drop the createAnnouncement x402 endpoint, and re-point the 402 challenge and request-format worked examples at createLab - drop the permission-matrix rows in authentication.md and roles-and-permissions.md, and the Contributor gate lists - strip the prose in the data/roles deep dives, the developer guide and the Molecule Skill page (Announce phase removed, steps renumbered) Kept deliberately: the LabEventAnnouncement / SearchLabsAnnouncementHit union variants and the ANNOUNCEMENT filter value, which callers still receive from labActivity and searchLabs regardless of what we document; the changelog and release-notes entries, which are shipped history; and the verbatim aura-orchestrator SKILL.md frontmatter quote, which is a reproduction of the plugin's own manifest. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The feature is no longer supported, so the schema-shape and historical references kept in 4e02a6d are gone too: - browse-and-search.md: the LabEventAnnouncement and SearchLabsAnnouncementHit union variants, the announcement-only labActivity curl example (now queries LabEventFileAdded), the attachments breaking-change note, the use-case list and the __typename switch in the JS example - authentication.md: the ANNOUNCEMENT filter values on labActivity and activities - changelog.md: the createAnnouncement gate-list entry (six writes -> five), the renamed-query and renamed-mutation rows, and the diff example (now finishCreateOrUpdateFile) - release-notes/labs-api.md: createAnnouncement in the write list - molecule-skill.md: the aura-orchestrator SKILL.md quote is now abridged and labelled as such, rather than quoting a stale manifest Also reworded two uses of the English verb "announced" (new error codes are "published" in the changelog) so the term does not appear at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| | **I have no credential, or I want to pay per call** instead of holding a long-lived token | **x402 gateway** — settle USDC on Base per request, no service token to provision | [x402 Gateway](../x402-gateway.md) | | ||
| | **I already made my lab in the app** (email sign-in, no wallet) **and now I want my agent writing into it** | **Agent-as-Contributor** — the human grants a role, the agent issues its own token | [Tutorial 3](tutorial-3-agent-access.md) | | ||
|
|
||
| The lanes compose. A common shape is the plugin lane for the workflow plus x402 for the paid mutations, which is exactly what the plugin does by default. |
There was a problem hiding this comment.
I don't think this reads well and is slightly confusing. I would change this to:
"The lanes are composable, so choosing one now doesn't lock you in to that lane only."
|
|
||
| ### 1. A `mol_` consumer credential — the one manual step | ||
|
|
||
| Every request to the API carries a consumer credential in the `Authorization` header. There is no self-service issuance yet, so this is the single "ask the team" left in the API docs. |
There was a problem hiding this comment.
The second part of this line reads more like a review of the docs rather than an instruction. Change:
"There is no self-service issuance yet, so this is the single "ask the team" left in the API docs."
To:
"There is no self-service issuance yet (coming soon), so you will need to request this from the Molecule team."
| | Your situation | Lane | Start here | | ||
| | -------------- | ---- | ---------- | | ||
| | **I run an AI coding agent** (Claude Code, Codex, Cursor) and want it to do the whole workflow | **Molecule Skill plugin** — a skill + MCP server that wraps every network, onchain and crypto operation as one tool call | [Molecule Skill](../../ai-tooling/molecule-skill.md) | | ||
| | **I'm scripting against the API** in Node/TypeScript and want to see the raw calls | **Raw GraphQL + viem** — self-issue a service token, mint, upload | [Tutorial 1](tutorial-1-public-upload.md) | |
There was a problem hiding this comment.
Change:
"| [Tutorial 1] ..."
To:
"| [Public File Upload Tutorial] ..."
| | **I run an AI coding agent** (Claude Code, Codex, Cursor) and want it to do the whole workflow | **Molecule Skill plugin** — a skill + MCP server that wraps every network, onchain and crypto operation as one tool call | [Molecule Skill](../../ai-tooling/molecule-skill.md) | | ||
| | **I'm scripting against the API** in Node/TypeScript and want to see the raw calls | **Raw GraphQL + viem** — self-issue a service token, mint, upload | [Tutorial 1](tutorial-1-public-upload.md) | | ||
| | **I have no credential, or I want to pay per call** instead of holding a long-lived token | **x402 gateway** — settle USDC on Base per request, no service token to provision | [x402 Gateway](../x402-gateway.md) | | ||
| | **I already made my lab in the app** (email sign-in, no wallet) **and now I want my agent writing into it** | **Agent-as-Contributor** — the human grants a role, the agent issues its own token | [Tutorial 3](tutorial-3-agent-access.md) | |
There was a problem hiding this comment.
Change:
"| [Tutorial 3] ..."
To:
"| [Agent Access Tutorial] ..."
|
|
||
| | Item | Cost | How we know | | ||
| | ---- | ---- | ----------- | | ||
| | **LabNFT mint** | Gas only. `mintFeeWei()` reads **0** on Base Sepolia **and** on Base mainnet (verified 2026-08-27 by `eth_call`) | Read it live yourself — the tutorials do, and send it as `value` | |
There was a problem hiding this comment.
Remove "verified 2026-08-27 by eth_call"
Change to:
"""
| LabNFT mint | Gas only on Base Sepolia and on Base mainnet | Read it live yourself using mintFeeWei(), and if non-zero, send it as value |
"""
| | ---- | ---- | ----------- | | ||
| | **LabNFT mint** | Gas only. `mintFeeWei()` reads **0** on Base Sepolia **and** on Base mainnet (verified 2026-08-27 by `eth_call`) | Read it live yourself — the tutorials do, and send it as `value` | | ||
| | **`createLab`, uploads and other content writes** (service-token lane) | Free | Consumer credential + self-issued service token | | ||
| | **The same mutations via x402** | Quoted per request in the `402` challenge — **$0.01 USDC** on both environments today | [Read the price off the challenge](../x402-gateway.md#reading-the-402-challenge); never hardcode it | |
There was a problem hiding this comment.
Change:
"Quoted per request in the 402 challenge — $0.01 USDC on both environments today"
To:
"Quoted per request in the 402 challenge, currently $0.01 USDC on both environments "
| ``` | ||
|
|
||
| {% hint style="warning" %} | ||
| **Two places the indexer trails, and both need that retry.** A successful response does not mean every downstream read is caught up yet: |
There was a problem hiding this comment.
This is unclear and poorly written, improve the wording:
eg:
"Onchain and offchain systems that work in parallel must be synchronised, and therefore there can be delays in data propagation. This means that a successful response does not mean that every downstream read has caught up yet"
|
|
||
| *** | ||
|
|
||
| ## Shared setup |
There was a problem hiding this comment.
What is this section and why is it here? It is unclear why this is relevant to the reader. It's important to imagine that the reader has zero knowledge of how Mol API works or why they are doing each action
ph0ph0
left a comment
There was a problem hiding this comment.
Review from my agent - please ask your agent to double check that they are valid and if so, fix them:
Issue 1 — WALLET_MISMATCH documented as a sign-in failure the resolver can never return (high)
Where:
- api-reference/getting-started/create-lab-and-upload-file.md:103
- api-reference/labs-api/service-tokens.md:111
- api-reference/changelog.md:22
- .github/prompts/docs-sync.md:68
Issue: All four state that generateServiceToken returns UNAUTHENTICATED / reason: WALLET_MISMATCH when "walletAddress isn't the signer". The resolver (desci-infra
lambda/appsync-resolver-labs-lambda/index.ts, signature path) emits exactly three reasons: NONCE_NOT_FOUND, NONCE_EXPIRED, INVALID_SIGNATURE. A wrong walletAddress recomposes the
sign-in message for the claimed address, verifyMessage fails, and the caller gets INVALID_SIGNATURE. WALLET_MISMATCH exists only in the legal-agreement resolvers' Privy-user
path (x-wallet-address header vs. the wallet named in the call). The commit's own "9/9 staging checks" never probed wallet-mismatch — because it's unreachable.
Why it matters: An agent that hits the wrong-address case gets INVALID_SIGNATURE, whose documented fix ("sign the most recent message byte-for-byte") never says to check the
address — while the branch that would have can never fire. And docs-sync.md:68 instructs the sync agent that these reasons "come from the generateServiceToken resolver", baking
the error into the source-of-truth map.
Fix: Delete the WALLET_MISMATCH rows from the three sign-in tables; fold the case into INVALID_SIGNATURE ("altered text, a superseded message, or a walletAddress that is not the
signer"). Drop it from docs-sync.md:68's list. Keep changelog.md:125's general catalogue row — the reason genuinely exists on the legal-agreement path.
Issue 2 — Bad expiresIn documented as VALIDATION_FAILED; it actually returns a retryable-looking INTERNAL_ERROR (medium-high)
Where:
- api-reference/getting-started/create-lab-and-upload-file.md:104
- api-reference/labs-api/service-tokens.md:100 and :113
- api-reference/authentication.md:154
Issue: expiresIn is never validated in the resolver — it flows into token-manager-service.generateServiceToken, where calculateExpirationDate (lib/utils/token-expiration.ts)
throws a plain Error on bad format or out-of-bounds values. The service's own catch wraps it with maskedErrorResult(..., { reason: "TOKEN_GENERATION_FAILED" }), and
classifyError's generic-Error fallback yields INTERNAL_ERROR with a masked message. So "3y" comes back as INTERNAL_ERROR / reason: TOKEN_GENERATION_FAILED — not
VALIDATION_FAILED.
Why it matters: The agent one-pager (correctly) classifies INTERNAL_ERROR as retryable, so an agent following these docs will retry a permanently-failing request with backoff
instead of fixing its input — the exact failure loop the error contract exists to prevent. It also means "our fault, file a bug" masking is hiding a client mistake — arguably a
backend bug worth its own ticket (validateExpirationFormat exists in the backend and is unused in this path).
Fix: Either fix the backend first (pre-validate expiresIn in the resolver) and keep the docs as-written, or correct the four doc sites to today's behavior. Given the webinar
deadline: correct the docs now ("today this surfaces as INTERNAL_ERROR / reason: TOKEN_GENERATION_FAILED; validate the format client-side") and file the backend ticket.
Issue 3 — reason: NOT_CONTRIBUTOR doesn't exist (medium)
Where: api-reference/getting-started/agent-as-a-lab-contributor.md:136
Issue: The indexer-lag hint says a pre-index write returns UNAUTHORIZED (reason: NOT_CONTRIBUTOR). That reason string appears nowhere in the backend — a membership denial is
code: UNAUTHORIZED with details.reason: "UNAUTHORIZED" (auth-service.ts's MembershipAuthReason map → membershipDenialApiError). The retry helper matches on the code, so the
script works; only the parenthetical is fiction — but it's exactly the kind of specific-looking token an agent will grep responses for. (Note: changelog.md:126 also lists
NOT_CONTRIBUTOR as an example reason under UNAUTHORIZED — same correction applies there.)
Issue 4 — example-workflow.md deleted with no redirect; the PR built the right stub and then deleted it (medium)
Where: commit 7b50197 deleted api-reference/labs-api/example-workflow.md; the repo still has no .gitbook.yaml at head 4974fda.
Issue: Example Workflow has been the published, SUMMARY-listed tutorial URL for months — the page external bookmarks, chat links and search results point at. An earlier commit in
this PR replaced it with a hidden "This page moved" stub (the correct GitBook pattern); a later commit deleted the stub, so the live URL will 404 after merge. GitBook Git-sync
does not auto-redirect removed pages. (IPNFT-api.md's deletion is fine — never in SUMMARY, so no published URL.)
Fix: Restore the 20-line hidden stub (now pointing at the renamed tutorial files), or add a .gitbook.yaml with redirects: { api-reference/labs-api/example-workflow:
api-reference/getting-started/README.md }.
Issue 5 — The oclId diagram is 6 hex chars short (low)
Where: api-reference/authentication.md:195
Issue: tokenId is an 80-bit field (ocl-id.ts: (id >> 160n) & ((1n << 80n) - 1n)), i.e. 20 hex chars. The diagram shows a 14-char field, so the pictured "oclId" is 58 hex chars —
an invalid 29-byte value on a page whose whole point is byte-level precision. The decoded values (1526, the trailing address) are right.
Pasteable comment:
▎ The tokenId field in this diagram is 14 hex chars, but it's an 80-bit field (ocl-id.ts: (id >> 160n) & ((1n << 80n) - 1n)) — 20 hex chars, making the full oclId 64. As drawn
▎ the example is a 58-char/29-byte value that validateOclId would reject. Should read 000000000000000005f6 for 1526.
Issue 6 — The announcements purge documents an API smaller than the live one, with no deprecation notice anywhere (medium-high, new in the last six commits)
Where:
- api-reference/labs-api/browse-and-search.md — the labActivity, activities, and searchLabs sections
- api-reference/x402-gateway.md:52-59 — endpoints table vs. the "single source of truth" sentence
- The absence: "announcement" now appears zero times across api-reference/ and release-notes/
Issue: I checked the current dev schema and gateway: createAnnouncement is still a live mutation, LabEventAnnouncement is still in the LabActivityNode union,
SearchLabsAnnouncementHit is still in SearchLabsHit, the ANNOUNCEMENT filter enum value still exists, and X402_WRITE_MUTATIONS still allow-lists createAnnouncement. Three
consequences:
- labActivity is now described as "the file-event timeline", but unfiltered it still returns announcement nodes — including ones this very PR's Tutorial 4 verification created on
staging. The docs simultaneously deleted the filter parameter documentation, which was the actual mechanism for getting the file-only feed the page now claims is the default.
Same for searchLabs, whose union is documented with one arm out of two — agents doing exhaustive union handling will hit typenames the docs never mention. - x402-gateway.md:59 still says "The allow-list above is the single source of truth in X402_WRITE_MUTATIONS" — directly above a table that now silently omits one of that
constant's six entries. The page contradicts itself. - The previous commit's promise ("retained as a reference for integrations that already use it") was reversed: tutorial-4 deleted, files.md's Create Announcement section deleted
— so a mutation that still works, and that these docs taught until this PR, now has zero documentation and zero deprecation notice, while the changelog's error-contract section
promises changes are "published on this page".
Fix: Distinguish "stop recommending" from "deny existence": one deprecation entry in the changelog, restore the filter parameter row on browse-and-search with deprecation
framing, and either mark the x402 table row deprecated or file the desci-infra change removing createAnnouncement from X402_WRITE_MUTATIONS so the "single source of truth"
sentence becomes true again.
Issue 7 — Dated historical records were rewritten to be false (medium, new)
Where:
- api-reference/changelog.md:38 — the IP-3013 role-parity entry
- api-reference/changelog.md ~190-198 — the V2-rename/migration tables
- release-notes/labs-api.md:17-20 — entry dated 2026-08-25
Issue: The role-parity entry now reads "The five content-write mutations" — the shipped change covered six, and createAnnouncement still gates a service token on Contributor
today (verified against the resolver gates). The V2 tables dropped the createAnnouncementV2 → createAnnouncement and projectAnnouncementsV2 → labActivity/activities (filter:
ANNOUNCEMENT) rows — a legacy integrator's only documented migration path. And a release note explicitly dated 2026-08-25 was edited to remove createAnnouncement from the list of
mutations the Assignment Agreement gate applied to, which it did. A changelog that retroactively changes what shipped is worse than one mentioning a deprecated feature — it's
the reference integrators check when old code breaks.
Fix: Restore the accurate lists and rows, annotated "(announcements since deprecated — see [entry])" rather than deleted. History entries state what was true at their date.
Issue 8 — docs-sync.md's README row describes date stamps that no longer exist (low, new)
Where: .github/prompts/docs-sync.md:56 vs. the refactored api-reference/getting-started/README.md cost table (~line 100).
Issue: The docs-sync row says the README's live values (mintFeeWei() readings, x402 prices) are "each stamped with the date it was checked" — the refactor removed those stamps
("verified 2026-08-27 by eth_call" and the price's as-of date are gone). The stamps were also the guard that row relies on ("never update one of those from a diff alone").
Pasteable comment:
▎ Small consistency catch: docs-sync.md's README row still says the live values here are "each stamped with the date it was checked", but this refactor dropped the stamps from
▎ the cost table (mintFeeWei() no longer says "verified 2026-08-27 by eth_call", the $0.01 lost its as-of date). Either restore the dates — they're also what makes "flag, don't
▎ edit from a diff" enforceable — or update the docs-sync row.
|
|
||
| Every request to the API carries a consumer credential in the `Authorization` header. There is no self-service issuance yet (coming soon), so you will need to request this from the Molecule team. | ||
|
|
||
| Request it on the [Molecule Discord](https://t.co/L0VEiy4Bjk) with this template: |
There was a problem hiding this comment.
This is just a general comment and not something i would like you to change right now as it will require an internal decision. When we have agreed with Kevin/Ella, we can create a new PR for this:
We don't mention which channel to post the request in, and the request doesn't tag anyone on Discord - this will probs cause requests to go unnoticed...
We need to discuss this internally, perhaps we have a dedicated channel or at least someone who should be tagged in the request so we don't miss any.
We should have self-service ready by EOY, so hopefully we won't need this for long.
There was a problem hiding this comment.
I will mention this to ella.
| | Staging | `https://testnet.labs.molecule.xyz/projects/<shortname>` | | ||
| | Production | `https://labs.molecule.xyz/projects/<shortname>` | | ||
|
|
||
| `shortname` is derived server-side from the lab's name and is `null` until that has happened, so a freshly minted lab is reachable by `oclId` before it is reachable by slug. |
There was a problem hiding this comment.
This is incorrect, a Lab page is never reachable by appending the oclId.
Before the user renames the lab, the url should be appended with lab-<tokenId>. Once the lab has been renamed, lab-<tokenId> will stop resolving.
| } | ||
| ``` | ||
|
|
||
| `shortname` is derived server-side from the lab's name and is `null` until it has been derived — that is expected on a lab that has just been minted and not yet named. |
There was a problem hiding this comment.
See my comment in Getting Started. This isn't true as it defaults to lab-<tokenId>
|
|
||
| That is exactly what happens here. `createLab` can succeed before your mint has been indexed, because it falls back to checking ownership onchain. The file mutations have no such fallback — they read the indexed record, and return `NOT_FOUND` until it arrives. | ||
|
|
||
| So wrap the first call in [`withIndexerLagRetry`](shared-setup.md). On staging the record usually appears within seconds, but one mint took **over four minutes**, which is why the helper keeps retrying that long instead of giving up after a few seconds. |
There was a problem hiding this comment.
We can remove the "...but one mint took over four minutes..."
Change this:
"So wrap the first call in withIndexerLagRetry. On staging the record usually appears within seconds, but one mint took over four minutes, which is why the helper keeps retrying that long instead of giving up after a few seconds."
To something like:
"Wrap the first call in withIndexerLagRetry so the helper will retry until the record arrives."
ph0ph0
left a comment
There was a problem hiding this comment.
Please merge once changes are made, and ensure that all my previous comments are resolved as well :)
| } | ||
| ``` | ||
|
|
||
| `shortname` is `null` here, which is expected on a lab that has just been minted and not yet named — it is derived server-side once the lab is given one. Note `labNftTokenId` in the response: until the lab is renamed, that is what its page URL is built from (see [Step 5](#step-5-verify-it-worked)). |
There was a problem hiding this comment.
This is incorrect still - when the lab is minted the event is indexed the BE seeds the shortname with a default, which is lab-<token-id>
|
|
||
| *** | ||
|
|
||
| ### 📊 IPNFT API (Deprecated) |
|
|
||
| *** | ||
|
|
||
| ## Then what |
| | `UPSTREAM_UNAVAILABLE` | A dependency is down (`reason: KAMU`) | `retryable: true` — retry with backoff | | ||
|
|
||
| DID-linking for the new lab starts automatically in the background; [`getDidLinkStatus`](../labs-api/lab-management.md#get-did-link-status) reports its progress. You do not need to wait for it. | ||
|
|
There was a problem hiding this comment.
Put a sentence here stating that if the user is wants to upload an encrypted their file, to jump to the upload-encrypted-file tutorial with a link - otherwise they should proceed.
| ## Obtaining API Access | ||
|
|
||
| All Molecule APIs require authentication with a consumer credential. To request access: | ||
| Every request carries a **consumer credential** in the `Authorization` header. Request one on the [Molecule Discord](https://t.co/L0VEiy4Bjk): post in [the API channel](https://discord.com/channels/608198475598790656/832947534983987281) and ping **@ella**, using this template (the channel link needs you to be in the server — join with the invite first): |
There was a problem hiding this comment.
We don't have an API Channel and this links to the General Chat Channel? I would maybe label this as General Channel so it doesn't confuse people and they go looking for the general channel
Self-service Getting Started for agents & humans on the Labs API — IP-3028.
Makes the API Reference self-service: a reader — or an agent — with only the docs URL and a
mol_credential can get to a lab with files in it.What's new
api-reference/getting-started/— "choose your path" for the four lanes, prerequisites with real costs, a ≤10-minute quickstart, and four tutorials: public upload, encrypted upload, agent-as-Contributor on a lab made in the app, announce. Plus an agent one-pager meant to be pasted into a system prompt.api-reference/;legal-agreements.mdis hidden and out of nav.Authorization: Bearercontradictions, the "service tokens are lab-scoped" error, broken anchors, and the error contract.api-reference/IPNFT-api.md, an orphan duplicate teaching the retiredx-api-key.Verified against staging
All four tutorials run end to end; 120/120 embedded GraphQL documents validate against the live schema; 0 broken links. Evidence: lab lab-1542, [mint tx]((https://sepolia.basescan.org/tx/0x65c63576e1ccbf90e86de9f21ea2785a865fba7b64b99e48699e2fb422635c82), public
datasetIddid:odf:fed01750c84c6aba2f13fda72de412cb858d034b3b2dc76a388f5e58fbe09c7a11474, encrypteddid:odf:fed01e35ce981a01fdfc57d037252bdf3bcf2775e0bb47b0a8222d851d3d0f735cacfwith a verified decrypt round trip. Tutorial 3 verified post-IP-3013 on testing-lab: an agent wallet granted Contributor in the app self-issued a token and uploaded into a lab it does not own.