Repository navigation
Lolly Work 1.2.0 pairs the Lolly Work server and render worker with
Lolly 1.2.0, built from
Lolly source b2128b260525e82cd6d7d29647eef915354ffc8f. It is published as
Lolly Work v1.2.0.
Installation and production acceptance are tracked separately.
Start with the tagged installation guide,
deployment options
and documentation index.
1.2.0 adds partner programmes. Resellers, distributors and other partners each get their own
tools, templates, files, company details and agent tokens. Files reach them by the licence each
file records, and a partnership can end on a set date and be reopened within 30 days. Service
tokens gain scopes and limits, an organisation MCP endpoint serves them, a reviewer approves the
exact exported file, and the durable render routes catch up with the older job API. Most of this
waits until an administrator sets it up. The changes that apply on upgrade are listed under
On upgrade below.
Matching components
| Component | Version or source |
|---|---|
| Lolly product version | 1.2.0 |
| Lolly Work application | 1.2.0 |
| Render worker and admission adapter | 1.2.0 |
| Lolly Work Helm chart | 1.2.0, with appVersion 1.2.0 |
| Lolly source | b2128b260525e82cd6d7d29647eef915354ffc8f |
| Engine | 1.343.0 |
| Tool-author core | 1.8.0 |
| Lolly web archive for YunoHost | lolly-web-1.2.0-ynh1.tar.gz, 808,658,284 bytes, SHA-256 3a324eef05128d32c6154ab88e360c40a8235ee181c3b49abe4ef5b556d1ac0f |
Engine and core versions describe their own contracts and are not reset to the
product version. engine-pin.json and content-resolver-pin.json record the
Lolly source above. The maintained producers generated both.
Lolly Work 1.1.3 carried engine 1.285.0 and core 1.5.0. From engine 1.343.0, Lolly Work uses
the approval record in Content Credentials (including the record for a template's variants),
the design system and colourway keys of the Lolly record, the licence reader and the
.lolly credits text, the intent checks for templates, the Design compiler that rebuilds a
Share with rules file, and the DESIGN.md writer. Core 1.8.0 carries the partner-kit-v1 format.
What Lolly 1.2.0 needs from this release
| Lolly 1.2.0 feature | What Lolly Work 1.2.0 adds |
|---|---|
| Approval of the file itself | The export-review approval subject: the exact exported file, frozen with the request and released by GET /api/v1/approvals/:id/artifact |
| "Until approved" shows PREVIEW until an approved request matches | Server enforcement of enforce.watermark: until-approved, and POST /api/v1/approvals/match for the export bar |
| Brand managers edit colours, colour roles and the brand page address with no rebuild | /api/v1/brand/edits, the brand.edit action and the brand-manager role |
| Publish a brand page to a Lolly Work instance | POST /api/v1/brand-pages and public pages at /brand/<slug>/, off unless brandPages.enabled |
| Workspace templates on the home shelf and in the Start chooser | Template entries in each caller's feed, with version, editable inputs, retirement, replacement and company bindings |
| A partner's logo and details already in place, the company card, and a home that keeps to what is theirs | org-config partner and programmeScoped, and per-tool defaults, bindings and locked values |
| View as partner | POST /api/v1/programmes/:id/view-as, the x-lw-view-as header and POST /api/v1/view-as/end |
| Partner kits in Design | org-config partner.company.kit and GET /api/v1/me/company/kit |
| A tool turned off for a group stays off, and an export rule for one tool holds | tool.use grants decide the tool feed and /tools/*, and org-config answers tools[<id>].can per tool |
| Links whose recipients can change the inputs | "open": true in a tool link's signed target |
| A release-built web app lists tools when the feed holds workspace files | Instance asset checksums served in SRI form |
The API detail behind each feature
- The exact file. An approval request of subject
export-reviewcarries the exported
bytes, sent by the parts protocol in the newapproval-artifactupload scope, which
policy.approvalArtifactsgoverns rather thanpolicy.uploads. Creating the request checks
the bytes and freezes them with their SHA-256, size, name, format, media type, tool id and
tool version.GET /api/v1/approvals/:id/artifactserves them inert and audited: before a
decision to eligible reviewers and people who acted, to the requester only once approved, and
never after a rejection or withdrawal. Approving needs the subject's fingerprint
(409 ARTIFACT_SUBJECT_CHANGED) and a token that comes only with the bytes
(409 ARTIFACT_NOT_LOADED), so nobody approves from the JSON alone. Inbox messages about such
a request carrydata: { kind: 'export-review', approvalId }. - The approval in the approved file. When the requester leaves the box ticked,
policy.approvalArtifacts.recordInCredentialsis on (the default), the server has a signing
identity and the engine can write the record, the approved download is the reviewed bytes with
a signed Content Credentials update that records the approval. In every other case it is the
reviewed bytes untouched, and the request gives the reason.?file=reviewedand
lw approvals artifact --reviewedstill give the reviewed bytes. - Until approved. A server render of a tool with
enforce.watermark: until-approved
carries the PREVIEW pattern unless an approvedexport-reviewmatches its tool id, tool
version, inputs digest and format. The app reports the inputs; the server works out the digest
with the render path's own steps and keeps no values.POST /api/v1/approvals/match(a
person's session only) answersnone,in_review,approved,rejectedorchangedfor a
tool, a format and a URL-mode query, and sends no bytes or digest. org-config advertises it as
approvals.exportReview.match, and discovery asapprovals.exportReviewMatch. Server
renders, share links and durable outputs sayx-lolly-watermark: Marked until approvedor
Approved, not marked, and those responses areprivate, no-store. - Brand edits. Routes under
/api/v1/brand/edits: state and history (GET), the
document, preview, send (POST), rollback, keep and settings. A sent document may differ from
the mounted one only in colours, colourways, colour roles and their bindings, and the brand
page address; anything else is refused by name. Work serves the edit from an immutable
revision over the mounted pack, so the catalogue, its signature, server renders, the Chromium
worker and rule checks all read the edit. Approval before an edit goes live and the
keep-or-roll-back question after a new image are both off by default, and changing either needs
policy.edit. A connect pack is derived only when this instance's key signed the kept
pack. - Brand pages. With
brandPages.enabled,POST /api/v1/brand-pagestakes a page folder as
a ZIP through the parts upload (scopebrand-page), checks every file againstbrand.json
and its digest, and keeps immutable revisions with one current revision per address. Pages are
served at/brand/<slug>/under a CSP that runs no script, listed at
/.well-known/lolly-brand.jsonand/api/v1/brand-pages/public, and read by agents at
POST /brand/<slug>/mcp. Publishing needs the newbrand.page.publishaction (admin), can
wait onbrandPages.approvalChain, and is closed to scoped tokens. The parser accepts the
logoslist,fonts/<family>-licence.txtandcolourRolesthat Lolly 1.2.0
writes. - Templates in the feed. A template published to a programme or company appears in its
people's feed withmeta.version,meta.editable,meta.retired,meta.replacedBy,
meta.bindingsand, where set,meta.intents. It never gives a rule's name or a reason. A Share
with rules file published the same way is served as a tool at/tools/<id>/*. Work rebuilds
that tool from the file's data with the pinned Design compiler, and never stores, serves or
runs the file'shooks.js. Where a programme keeps a tool to its templates, the tool index
marks the toollisted: falseandtemplatesOnly: true. - The partner in org-config. For a programme member,
GET /api/v1/org-configcarries
programmeScoped: trueandpartner: { company, programme }: the company's id, name,
profile revision, logos, website, contact, address, currency and tax note, plus
partner.accessEndsAtandpartner.accessEndsThroughwhen an end is due. A tool with company
bindings getstools[<id>].defaultsandtools[<id>].bindings, and a bound input the rules
lock arrives asaccess: { level: 'locked', value }. No input in org-config carries a rule's
name or reason. - View as partner.
POST /api/v1/programmes/:id/view-as, with an optionalcompany,
returns a one-hour token bound to the admin's session. The app sends it from one tab in
x-lw-view-as. Only a partner's reads answer, as the partner and limited to what the admin may
also see: org-config (withviewingAsand everycanbit false), the inbox,/catalog/*,
/tools/*, catalogue browse, the AI policy and the company profile. Writes and other reads
answer403 VIEWING_AS, another session403 VIEWING_AS_MISMATCH, and an ended view
403 VIEWING_AS_ENDED.POST /api/v1/view-as/endends the view for good. - Partner kits. org-config carries
partner.company.kit: { id, revision, label }for the
company's members, andGET /api/v1/me/company/kitreturns the kit. - Tool grants. A
tool.usedeny now hides a tool from the tool feed, its signature and
sidecars, tool art, share cards and/tools/*, as it already did from Preview, server renders
and the organisation MCP. An allow outside the overlay's visibility clause shows the tool.
org-config addstools[<id>].canwhere anexport.downloadorexport.requestgrant for one
tool gives a different answer from the deployment-wide bit. Older apps read only the
deployment-wide bit. - Open links. A
share,embedordownloadlink to a tool minted with"open": truein
its target lets recipients add the tool's URL params, each replacing the baked value, and each
set renders a new asset with its ownETag. Locked, hidden and disallowed inputs still answer
422 (INPUT_LOCKED,INPUT_HIDDEN,INPUT_NOT_ALLOWED). The flag is signed into the target,
opening a link needsexport.serverfor the tool as well aslink.create, and guest, asset
and collection links cannot be open.lw links create <toolId> … --openmints
one. - SRI checksums. The served asset index gives instance assets (
inst/*) their checksum as
sha256-<base64>, the formschemas/asset.schema.jsonstates, instead of the blob store's
hex. A release web app refused a whole index over one hex row. Stored records and blob ETags
are unchanged. - Ask an agent, ready for the desktop apps. The AI policy accepts the
agentscapability,
off by default and matched exactly, for the desktop apps' Ask an agent in their next build.
Other changes since 1.1.3
Partner programmes
- Programmes. An administrator, or anyone granted
programme.manage, creates a programme
for each kind of partner. It wraps the reserved local groupprogramme-<id>. Its people see
only the tools atool.useallow grant gives that group, the design system's essentials, and
the collections, assets and provider folders shared with the programme. "Everyone" no longer includes
them, and*in a group list now means everyone internal. Joining adds apartnermarker
that stays after they leave. Preview shows what each group gets. - Input rules for partners. Within one programme, a rule for the person's company overrides
the programme's rule in either order. Across programmes the stricter result wins, and a partner
rule beats a generic one. Everyone else keeps the overlay's first-match order. Preview tells an
admin which rule decided, with its name and reason. - People and companies. Companies sit inside a programme as
company-<id>groups. People
are added from pasted text or a CSV (email, name, company, end date), previewed row by row and
applied withapply: true; the same list twice changes nothing, and a row naming an admin or
owner is refused. Memberships can carry an end date, which a sweep applies every
minute. - Partner domains. A partner's domain can feed a programme, with an optional company and end
date. Proof is a DNS TXT record,_lolly-work.<domain> TXT "lw-verify=<token>", or an
admin's audited say-so. Anyone who signs in with a provider-verified address at that exact
domain joins. Public mail domains, shared suffixes such asco.ukand the workspace's own
domains are refused. Staff accounts already here are never moved unless an admin chooses
moveStaff. A domain whose record disappears pauses. - Provider folders. A rule gives one folder of a connected provider, and everything below
the folder, to a programme or one company until an optional end date. Drivers now report a
folder per asset: S3 and Dropbox the full key prefix, WebDAV the directory, OneDrive and
SharePoint the path, Brandfolder the section. Google Drive gains an opt-in recursive walk
(recursive,maxDepthdefault 4,maxFoldersdefault 500). A rule must name a folder; the
whole provider takeswhole: true. - Templates. A brand team publishes a catalogue template or a Share with rules file to a
programme or company, chooses what partners may change, versions the template and retires it
with an optional replacement. A render namingtemplate=inst/<id>starts from the template's
values and answers422 INPUT_LOCKEDfor a fixed input,404 TEMPLATE_NOT_FOUNDoutside the
caller's feed,422 TEMPLATE_TOOL_MISMATCHfor another tool and410 TEMPLATE_RETIREDwith
replacedByonce retired. A rules file that loads anything from outside itself, is damaged,
comes from another tool's session, reuses an id or is not newer is refused at upload. The
templates-only switch, per programme and tool and off by default, makes a render that names
none of their templates answer422 TEMPLATE_REQUIRED. Publishing records the workspace's
owner-published mark on its own work unlessownerMark: false. - Company profiles and values. Each company gets logos for light and dark surfaces, an
optional mark, a website, contact, address, currency and tax note, kept as numbered revisions.
A logo is SVG up to 512 KB or PNG up to 2 MB and at least 400 px on its long side. SVGs are
cleaned with the web app's own DOMPurify profile, and active content or references to other
files are refused; low contrast and opaque backgrounds are warnings only. Templates and tool
overlays bind inputs to company fields, and one resolver fills them for renders, org-config,
Preview and the agents' schema. Editing by a company's own contacts is off by default, with an
optional approval chain. - Partner kits. One kit per company in the
partner-kit-v1format: logos pinned by
version, colours, and rules such as clear space, minimum size and treatments, which a person
types. An import from a tokens file, a.lollyshare or the company's SVG logos stores nothing
until saved. Kit changes can wait for approval (settings.kitApprovalChain, off by default),
ending a partnership retires the kit, and Reopen restores the retired
revision. - Licences decide what reaches partners. With
policy.catalog.partnerRights: gate, a file
reaches a partner, or anyone holding a link, only when its recorded licence lets the bytes
travel, the brand team marked the file as its own, the file is exempt, or an admin shared it
anyway with that audience. Exempt types aretokens,font,palette,template,
rules-tool,user-toolandskill, plus a company's own files for that company. A server
render or compile that needs a held file answers422 ASSET_HELD. Marks cover one asset, a
provider folder, a provider or a pack id prefix, and never reach a file that records its own
licence. A share-anyway choice retires when the file, its licence record or the rules change.
Staff receive what they did before.GET /api/v1/programmes/:id/rights-reportand
lw programmes rights-reportshow what the gate holds or would hold. - Credits. Every collection ZIP a link serves starts with
CREDITS.txtandcredits.json,
listing only the files in that ZIP. The collection link page shows one credit line where a
licence asks for a credit. - Ending a partnership. Ending a programme or company marks it ended and keeps one receipt
per exit. It offboards the people whose access ends, revokes the agent tokens placed there,
retires templates published only to the selection, ends its folder rules and withdraws its
share-anyway choices. An end can be scheduled for the end of a UTC day, and people are told 14
days and 1 day before, in the inbox and by email wherenotify.people.emailis set up. Within
30 days, Reopen brings back the accounts, memberships, templates, folders and projects the
receipt lists; links, tokens, sessions and share-anyway choices stay
ended. - Partner scope on every route. A partner's reads follow their own served feed: instance and
submission bytes, asset records, pack files no asset owns,cms://sources, live search,
submit duplicates, collections, lifecycle,/catalog/ext/...and brand profiles. Anything
outside the feed answers a plain 404 "not available", and/connect/pack.lollyanswers 403.
A programme-scoped person, token or view can only hold the actions inPARTNER_ACTIONS,
whatever a role default or grant says;group.createanddelivery.createare among those
withheld. Staff are unchanged. - Partner agents. A programme's manager mints, limits and revokes tokens for a partner's
agent. Such a token has theautomationrole, scopesrenderandcatalog.readby default
withdocuments,filesandapprovals.requestoptional, and a 90-day lifetime by default
(1 to 365). Limits default to 120 calls a minute and 2,000 renders a UTC day with no monthly
cap, and can be set per instance, programme or token. Usage counts per token, company and
programme, with a CSV download, never hold an input value, prompt, title or file
name. - Console. Partner programmes pages with Templates, People, Domains, Provider folders,
Licences, Agent tokens, What agents did and End cards, and a page per company with Details,
Logos, Contacts, Partner kit, Agent tokens and End. Preview draws each template exactly as a
chosen company receives the template. - CLI.
lw programmes,lw companies(withprofileandkit),lw templates,
lw people import|exit|exits|reopen,lw domains,lw foldersandlw catalog rights, and
programme placement, limits and usage onlw tokens.
Service tokens and agents
- Scopes. A service token can name what it may reach:
render,documents,files,
delivery,catalog.read,audit.readand the opt-inapprovals.request.
Scopes and an optional tools list are fixed at mint. A route missing from the scope table
answers403 TOKEN_SCOPE, and scopes only narrow what the token's role allows. The new
token-onlyautomationrole holdstool.use,export.server,delivery.create,
catalog.readandaudit.export, and can only be minted with scopes. A token with no scopes
behaves as before. - Organisation MCP.
POST /api/v1/mcpserves service tokens withlist_tools,
describe_tool,validate,render_tool,get_render,get_render_output,
cancel_render,submit_batch,get_batch,search_assets,list_destinations,
deliver_renderandresolve_intent, plusrequest_approval,get_approvaland
withdraw_approvalfor a token holdingapprovals.request. Each call runs through the router
with the caller's own token, so scopes, role, tool list, visibility and input policy apply as
over HTTP, and a tool the token cannot reach is not listed. A person's session gets 401. A
person's agent invitation stays on/api/workspace/mcp. - Brand context for agents. MCP resources
lolly-work://me,lolly-work://brandand the
brand's DESIGN.md, tokens, AGENTS.md, checks and skills, and for a partner token its company,
logos, templates and kit. The same reads are atGET /api/v1/context/*, for service tokens
only. Brand page files reach an agent only while the page is public. - Intents.
POST /api/v1/intents/resolve(scoperender) says which tool the caller can
render a piece with on each surface, with the input values and what is missing. The pinned
engine decides with no model, and the answer leaves out tools and inputs the caller cannot
use. Company values count as met, and published templates with purposes are candidates.
Nothing is rendered, kept or audited.lw intent resolveexits 0 when every surface is ready
and 4 otherwise. - Agents asking for review. A token holding
approvals.requestfiles an export review on
one of its own finished renders; the reviewed file is that render's output, checked against its
receipt. A partner token uses the tool's chain or one its programme lists (approvalChains),
else403 CHAIN_NOT_FOR_PROGRAMME. A delivery to a destination with a chain then waits for a
person instead of answering403 HUMAN_APPROVAL_REQUIRED. Deciding stays with
people. - One approval for a template's variants.
enforce.approvalCovers: template-variantson a
tool overlay (defaultfile) lets one approval of a template version cover every render from
that version, whatever partners put in its open inputs. A new version, a change to what is open
or a different fixed value needs a new approval. Such renders recordtools.lolly.approvalin
their Content Credentials withcovers: 'template', the template id and version, and the
example file's digest.
Renders
- Durable renders catch up.
/rendersand/render-batchesgain signed callbacks
(callbackUrl),bindon batches from a JSON or CSV source, a ZIP of a finished batch and
delivery by reference. The callback uses the job callbacks' outbox, signature and retry
ladder, and carries a 24-hourx-lw-render-tokenfor that render's output or that batch's
archive. Nothing is removed from/jobsor/batch. - Moving from a hosted render API. Bannerbear V5, Bannerbear V2 and Placid request shapes
on/compat/, off unlessrenderApi.enabled, with the durable render path underneath. Only
lwt_tokens are accepted. A property the mapping does not name is refused with
422 PROPERTY_NOT_MAPPED.lw render-api inventory|check|sample|comparelists templates,
checks mappings and compares output. The adapters come from public documentation and fixtures
and have not run against a live account. - HTML-layout tools render on the server. A tool with no hooks whose template has no
<svg>now goes to the Chromium worker. Without a worker it answers
501 SVG_TEMPLATE_REQUIREDinstead of a bare 500. - Design token colours. Worker renders keep a colour input that points at a design token, such
asink={color.brand.mint}, instead of falling back to the tool's default. - Server renders of hooked tools work again. The worker's request budget per render
follows the app files the shell lists inprecache.json, plus 256, between 512 and 4096, and
1024 when the list cannot be read. A flat 512 refused QR codes and Design rules tools with "A
render request could not be completed." This affected Lolly Work 1.1.1 to 1.1.3. One render
now opens at most 32 requests at a time. - A busy worker is busy, not broken. The worker's
/readyzanswers 200 while its browser is
connected and reportsatCapacity, so a full worker keeps its place in the Service. When the
server cannot connect to a worker, it retries with backoff for up to
render.worker.connectRetryMsand then answers503 RENDER_BUSYwithRetry-After: 5
instead of 502. - What a server render records. A signed server render's Content Credential carries the
design system, its label and the colourway (designSystem,designSystemLabel,themes), and
for a partnerpartnerCompany,partnerCompanyLabel,partnerProfileRevision,
partnerProgramme,partnerProgrammeLabel,partnerKitandpartnerKitRevision. It never
records the person, a token, an agent or the contact's details.
Public files behind a CDN
- Bunny and Cloudflare. An instance can put either provider in front of its public files,
configured in theedgeblock ofinstance.json. Purges by tag or URL go through an outbox
within each provider's budget when the brand, a pack, an overlay or the governance document
changes; a purge never blocks or reverses that change. Tag headers (CDN-Tagfor Bunny,
Cache-Tagfor Cloudflare) go only on answers a shared cache may store. - Jurisdiction. Under the default
eu-only, a provider whose company, edge locations or
logs are not in the EU may front only a hostname markedpublicOnly, and the instance's own
hostnames can never bepublicOnly. On such a hostname credential headers are removed, an
answer that is not public becomes an empty404 PUBLIC_HOST_ONLY, and writes answer 405. Bunny
counts as EU only when restricted to Europe with its logs stated as EU. - Checks.
GET /api/v1/edge/providers/:id/verifyreads a zone's settings without writing,
and reports image and page rewriting such as Bunny Optimizer and Cloudflare Polish, Mirage,
Rocket Loader and Email Obfuscation.lw edge probechecks from outside that private answers
and errors are not cached and that security headers pass through. An unreadable setting is
never a pass.
Permissions, links and errors
- Links stop with their maker.
GETandPOST /l/:idanswer410 LINK_MAKER_DISABLED
while the account that made the link is disabled, and work again when the account is
re-enabled. Offboarding still revokes links for good. - Grants that can never match.
POST /api/v1/grantsrefuses a rule whose resource the
server never checks the action against, with400 GRANT_NEVER_MATCHESand the forms that do
narrow.tool:tag/<tag>andcatalog:tag/<tag>were two such forms.GET /api/v1/grants
marks stored onesneverMatches, and the console says "Never matches". - Locks stay out of the artwork's way. org-config no longer carries the overlay's name or
the rule's reason on a locked input, so the app prints no policy line under the input. The
console's policy editor, the422 INPUT_LOCKEDbody and the automation contract keep
both. - A missing tool in plain words. Every route that loads a tool answers
404 TOOL_NOT_FOUND
for a missing one and500 TOOL_UNAVAILABLEwith one sentence for another filesystem failure.
No answer gives a server path. An id that cannot name a tool directory is refused before
anything is read. - A pack with two owners for one file.
inspectPacknow refuses an asset index where two
ids claim one catalogue file and gives the file and both ids. Before, such a pack mounted and
its catalogue files answered 503.
Retention and housekeeping
- Deleted documents can purge themselves.
policy.retention.deletedDocumentsDayslets the
retention run remove deleted documents older than the window, in batches of 100. It is off at
0, the default. Each run that purges writes aretention.runaudit entry, and the readiness
report flags an unset window for the internal profile. - Render output retention.
policy.retention.renderOutputDaysexpires finished render
outputs after that many days, while the record, receipt and identity stay. It is off at0,
the default. An output a delivery points at is never removed. - Migrations wait for the network. The migration runner retries a connect refused with
ECONNREFUSED,EHOSTUNREACHorENETUNREACH, up to 20 times 500 ms apart, which covers a
K3s NetworkPolicy that lets a new pod through a moment after its container starts. Any other
error still fails at once. - Messages with an end.
lw msg sendtakes--startsand--ends, as a date and time with
its zone. - Colourway asset treatments.
colourway-asset-treatmentsjoins the governable flags, on by
default. An admin can force the flag off and hide its toggle. The web app reads the flag once
it offers colourway colours for icons and photos. - Console. The packaged DAM guidance opens with a chooser that lists the services an
organisation can run itself first, and the console's guides land on links written in Lolly's
anchor form. - The local demo.
pnpm run demobuilds its pack view from hard links, so its tool feed
serves again.
Changes for API clients and operators
New routes
- Tokens.
POST /api/v1/tokenstakesscopes,toolsand limits.
PUT /api/v1/tokens/:id/programme,PUT /api/v1/tokens/:id/limitsand
GET /api/v1/tokens/:id/usage(owner). - Agents.
POST /api/v1/mcp, andGET /api/v1/context,/context/me,/context/brand
(with/design-md,/tokens,/agents-md,/checksand/skills[/:slug/*]),
/context/partner/company[/logo/:surface],/context/partner/templatesand
/context/partner/kit[/logo/:surface]. - Renders.
GET /api/v1/render-batches/:id/archive,POST /api/v1/renders/:id/deliveries
andPOST /api/v1/intents/resolve.GET /api/v1/schema/:toolId?template=inst/<id>returns
the schema a render from that template takes. - Migration API.
/compat/bannerbear/v5/images,/compat/bannerbear/v2/images, their
sync hosts,/compat/placid/api/rest/imagesand/compat/placid/u/:uuid, all answering 404
untilrenderApi.enabled. - Approvals.
GET /api/v1/approvals/:id,GET /api/v1/approvals/:id/artifact,
POST /api/v1/approvals/:id/withdrawandPOST /api/v1/approvals/match. - Brand.
/api/v1/brand/editsand itsdocument,preview,rollback,keep,
settingsandrevisions/:nroutes;/api/v1/brand-pageswith:slug/revisionsand
:slug/unpublish;/brand/:slug/*,POST /brand/:slug/mcp,/.well-known/lolly-brand.json
and/api/v1/brand-pages/public. - Catalogue rights.
GET,PUTandDELETE /api/v1/catalog/rights/marksand
/api/v1/catalog/rights/licences(catalog.edit), andGET /api/v1/catalog/rights/asset,
/coverageand/collections.POST /api/v1/links?dryRun=1lists which files a link would
carry. - Programmes.
GET /api/v1/programmes, andGET,PUTandDELETE /api/v1/programmes/:id
withcompanies/:company(and itsprofileandkit),people,folders,templates,
templates-only/:tool,tokens,usage,rights-report,rights/choices,exitsand
view-as.POST /api/v1/programmes/exit,GET,PUTandDELETE /api/v1/programmes/exits/:id,
POST /api/v1/programmes/exits/:id/reopenandPOST /api/v1/programmes/sweepfor a
serverless cron./api/v1/partner-domains[/:domain[/check]]./api/v1/me/company/profile
and/api/v1/me/company/kitfor a company's own people.POST /api/v1/view-as/endand
POST /api/v1/org-config/preview/render. - Edge.
GET /api/v1/edge,GET /api/v1/edge/providers/:id/verify,
POST /api/v1/edge/purgeandPUTandDELETE /api/v1/edge/providers/:id/credential, all
underinstance.config. - Limits.
GET /api/v1/limits(policy.edit) lists each enforced limit with its value and
its source.
New configuration
policy.retention.renderOutputDaysandpolicy.retention.deletedDocumentsDays, both0
(keep) by default.policy.approvalArtifacts:enabledtrue,maxFileBytes64 MiB,instanceBudgetBytes
1 GiB,closedRetentionDays30,approvedRetentionDays0(keep) andrecordInCredentials
true. A withdrawn request's file goes at once.policy.catalog.partnerRights:reportorgate. Unset, the instance's own recorded default
applies.automation.callbacks.allowedOrigins, empty by default: exact HTTPS origins that render and
job callbacks may reach besidenotify.webhook.url. A URL elsewhere answers
403 CALLBACK_URL_NOT_ALLOWED, redirects are refused, and origins withoutLW_WEBHOOK_SECRET
stop the boot.automation.tokenLimits:callsPerMinute120,rendersPerDay2000 andrendersPerMonth
null, for tokens in a programme. Over a limit a token gets429 RATE_LIMITEDor
429 BUDGET_EXHAUSTEDwithRetry-After.rateLimit.enabled: falsedoes not turn these
off.render.worker.connectRetryMs, default 30000, from 0 to 120000.renderApi:enabledfalse,mappingsFile,templates[],resultUrls.signedDays30
(0means result URLs need the bearer token) andurlApi.actAs.brandPages:enabledfalse,maxBundleBytes64 MiB,maxFileBytes32 MiB,
instanceBudgetBytes1 GiB,keepRevisions10 and an optionalapprovalChain; and
rateLimit.brandPagewith capacity 240 and 8 a second.edge:jurisdictioneu-only(orany) andproviders[], each withid,kind
(bunnyorcloudflare),hostnames,publicOnly,credentialRef,optionsand
jurisdiction. Unknown keys fail the load. A credential can also be sealed in the database
withlw edge credential.- Tool overlays:
enforce.escalationis writable and must name a chain that exists
(400 CHAIN_NOT_BOUNDotherwise),enforce.approvalCoversisfileortemplate-variants,
bindingsmaps inputs to company fields, anddefaultstakes the shape
[{ groups, values }]. - Programme records:
tokenLimits,approvalChainsandtemplatesOnly. - Roles and actions:
brand-manager(member plusbrand.edit,brand.page.publishand
brand.switch), granted only through an explicitidp.roleGroupsmapping;brand.edit,
brand.page.publishandprogramme.managefor admins; and the token-onlyautomation
role.
New environment variable
LW_RENDER_API_URL_SECRETsigns migration API result URLs. WithrenderApi.enabledand
renderApi.resultUrls.signedDaysabove 0, the server refuses to start without this
secret.
Changed behaviour for API clients
- Schema and the document verbs answer
404 TOOL_NOT_FOUNDfor a missing tool, where they
answered400 DOCUMENT_API_ERROR. A failed read that is not a missing file answers 500, so a
durable render retries it within its attempts. POST /api/v1/rendergives the refused input in its 422, asGETalways did, and sends
Retry-AfterwithRENDER_BUSY.- The served asset index writes instance asset checksums as
sha256-<base64>. - org-config carries no
byorreasonon an input. /discovery/meaddstoken: { scopes, scopesVersion, tools }, and itscanbits, like
can.renderin/discovery/toolsand/schema, reflect what the scopes reach. A scoped
token cannot readview=rawof a schema.- Instance asset bytes answer
private, no-cacheinstead ofprivate, max-age=300, and so do
/catalog/ext/...bytes for programme members. Staff keep five minutes on
/catalog/ext/.... - Without a configured edge provider, no answer carries
Cache-TagorSurrogate-Key. Brand
chrome and the hashed connect pack used to send both on every answer.
On upgrade
- "Until approved" starts marking. An instance that already holds
enforce.watermark: until-approvedshows PREVIEW on server renders and share links from the
first render after the upgrade, because the setting was stored and not enforced until now.
Change the rule first if that is not wanted. - Packs with two owners for one file stop a production boot. A production server exits
before it reaches the database and prints the file and asset ids. An evaluation server boots
with a warning on thepacksetup check, and its catalogue files still answer 503. Lolly Demo
packs built from Lolly 1.1.2 or 1.1.3 are affected. Runnode scripts/inspect-pack.ts <pack>before upgrading, and
rebuild a refused pack from Lolly 1.2.0. - Tool grants reach the app. A stored
tool.usedeny now hides the tool in the app as well
as in Preview, and a deny for everyone (*) reaches anonymous callers on an open deployment.
Reviewtool.usegrants before upgrading. - Grants that never matched are marked. Stored rules stay and are listed with
neverMatches.POST /api/v1/config/applyre-applies one already stored and refuses a new one,
andLW_SEED_CONFIGskips one with a warning. - Links stop with a disabled maker. Links made by an account that is disabled at upgrade
answer 410 until the account is re-enabled. - The licence gate reports first. On its first start, 1.2.0 records the gate's mode once:
gate
when the database holds no accounts andreportwhen it does. An upgraded instance therefore
changes nothing partners receive, and itspartner-rightssetup check warns with what the gate
would hold back. Setpolicy.catalog.partnerRights: gateto gate. The chart, Compose and
appliance templates do not set the key, so re-applying them cannot flip the
mode. - The approved file records the approval. A server with a signing identity now adds the
approval to an approved file's Content Credentials unless
policy.approvalArtifacts.recordInCredentialsisfalseor the requester opted
out. - Signed server renders record more. Their credentials gain the design system and colourway
keys, so their bytes differ from 1.1.3's. - Render worker image. The worker's readiness and request budget change in the worker
image, so update the worker with the server. With several worker replicas, a job can now reach
a busy pod and get503 RENDER_BUSY, where a busy pod used to leave the
Service. - A database that is down takes about 10 seconds to fail on the migration runner,
lw migrate,
the server's auto-migrate and the serverless function's bootstrap, instead of failing at once.
Migrations
This release adds 21 SQL migrations since 1.1.3:
0077_edge_purge_outbox.sqladdsedge_purge_outbox, the queue of CDN purges, and
edge_provider_credentials, edge credentials sealed with the instance credential key.0078_token_scopes.sqladds thescopesandtoolscolumns toapi_tokens.0079_durable_render_parity.sqlletsautomation_callbacksname a render or a render batch
as well as a job (job_idbecomes nullable, with a check that exactly one subject is named),
addsdeliveries.source_render_id, and adds an index for output retention.0080_approval_artifact_uploads.sqladds theapproval-artifactscope to theuploadsscope
check.0081_render_api_records.sqladdsrender_api_records, one row per migration API request.0082_approval_export_review_inputs.sqladds a partial index onapprovalsby the export
review's inputs digest.0083_brand_pages.sqladdsbrand_pagesandbrand_page_revisions, and thebrand-page
scope to theuploadsscope check.0084_brand_edits.sqladdsbrand_edit_revisions.0085_partner_programmes.sqladdspartner_programmes.0086_programme_people.sqladdspartner_companiesandprogramme_memberships.0087_partner_domains.sqladdspartner_domains.0088_partner_folders.sqladdspartner_folders.0089_company_profiles.sqladdspartner_company_profilesand
partner_company_profile_history.0090_token_groups.sqladds thegroupscolumn toapi_tokens.0093_programme_exits.sqladdsprogramme_exits, one receipt per ended or scheduled
partnership.0094_token_usage.sqladds thelimitscolumn toapi_tokensandtoken_usage, one row per
token and UTC day.0095_programme_tokens.sqladds theviacolumn toapi_tokens.0096_asset_rights_marks.sqladdsasset_rights_marksandasset_licence_records.0097_programme_rights_choices.sqladdsprogramme_rights_choices, the share-anyway choices.0098_instance_defaults.sqladdsinstance_defaults, where the licence gate's mode is
recorded on first start.0099_company_kits.sqladds thekitcolumn topartner_company_profiles.
The numbers 0091 and 0092 were never used, and the runner accepts the gap. pnpm run migrate:classify classes 17 files as additive and four as maintenance: 0079, 0080, 0082
and 0083. An install that sets LW_MIGRATE_EXPECT lists all 21 files or their manifest. An
older release can run on the new schema, with one limit: rolled back while render callbacks are
pending, it records those entries as failed and not-sent without sending them.
Before upgrading, check the database with pnpm run migrate:status, take a fresh verified
backup and complete an independent restore. Then apply the maintained migration runner or
the chart's migration Job, against the selected instance only. The maintained history now
has 88 files.
Deployment
- Helm chart 1.2.0. The chart's version is the release version. It gains no values or
templates. The render worker's readiness probe comments describe the new readiness, and
values-internal.yamlnamesretention.deletedDocumentsDaysamong the retention settings to
approve.LW_RENDER_API_URL_SECREThas no chart value of its own; supply it through
extraEnv. The chart is published throughpublish-helm.ymlfrom the release
commit. - Admission policy, K3s and the admission adapter. The Kubewarden policy set and the K3s kit
are unchanged since 1.1.3, and the admission adapter changes only its version. - The appliance. On k3s,
lolly-update checkverifies the published chart through the
channel and installs the chart without--keep-chart, as the 1.1.2 to 1.1.3 acceptance
showed on aarch64 and on x86_64.accept-updates.pyreads the starting release from the
machine's update state. No appliance image is published. - The demo pack is Lolly Demo.
packs/demo, which the server image, the chart's default
pack and the appliance mount, now holds Lolly Demo in place of SUSE, and the
lolly-demo-brand-1.1.3.lollyinstance pack replacessuse-brand-1.0.0.lolly. An evaluation
instance on the default pack
shows the Lolly wordmark and Lolly Demo's colours after the upgrade. The console and sign-in
pages read a design system's colourways. - YunoHost. 1.1.3~ynh2 set
LW_CATALOG_SIGNING_KEYto a key generated for each
installation, withinstance.catalogPublicationGenerationandinstance.catalogPublicKey, so
the app lists tools on YunoHost 13. The package
now also finds a Nextcloud on the same server and prefills Configuration > Nextcloud.
Once an administrator turns on Connect Nextcloud and pastes an app password, the panel
writes a config-managed WebDAV catalogue source and delivery destination toinstance.json,
with the password reaching the server only asLW_PROVIDER_NEXTCLOUDand
LW_DESTINATION_NEXTCLOUD. nginx and the SSOwat permissions pass/brand/<slug>/and
/.well-known/lolly-brand.jsonto the server. The shell headers keephttps://lolli.lifor
the on-device models. - Docker Compose. An optional
garageprofile adds a single-node Garage S3 store on the
Compose network only, forblobs.driver: "s3", withGARAGE_RPC_SECRETand
LW_BLOBS_S3_CREDENTIALin.env. It starts only with--profile garage. - Coolify.
deploy/coolify/lolly-work/docker-compose.ymlis generated from the same
services as the Dokploy blueprint. It routes the domain withSERVICE_FQDN_SERVER_8787, takes
the database password and the session, link and credential secrets from
SERVICE_PASSWORD_64_*, and will not deploy untilLOLLY_OWNER_EMAILis set. It is imported
as a Docker Compose resource and has not yet been run on a live Coolify host. - Caddy. The VM Caddyfile sends
/compat,/brand/<slug>/and
/.well-known/lolly-brand.jsonto the server. - The ready-made package. Its pins moved to the 1.1.3 pair in October and move to the
1.2.0 pair after this release is published.
Prepare and qualify
This section is for Lolly Work maintainers cutting the release.
Maintainer commands
The commands are the same as for 1.1.3, with vendor/lolly at the Lolly source
above:
node scripts/vendor-content-resolver.ts --write
pnpm run repin-engine --apply
pnpm run verify:engine-pin
pnpm run typecheck
The packaged documentation comes from a Lolly commit that contains this page:
node scripts/sync-docs-bundle.ts --source=/path/to/lolly --commit=<full-commit-sha>
pnpm run verify:docs-pin
Before tagging or publishing images, the exact final Lolly Work commit needs ordinary
CI, including the native server image checks and the real Chromium worker checks
with the matched signed shell. The tagged server and worker images must come from
that same Lolly Work commit.
YunoHost packaging
Lolly 1.2.0 publishes a new web archive, and the Lolly Work YunoHost package pins the
lolly-web-1.2.0-ynh1.tar.gz archive above. The maintained producer cuts the package on
the final Lolly Work commit:
node scripts/yunohost-release.ts --ynh-rev 1 --shell-url https://lolli.li/lolly-web-1.2.0-ynh1.tar.gz --shell-sha256 3a324eef05128d32c6154ab88e360c40a8235ee181c3b49abe4ef5b556d1ac0f --out ABS_NEW_ARTIFACT_DIRECTORY
Commit the generated manifest, recut into a separate directory and require full
archive byte equality, as for 1.1.3. The package is mirrored to lolly-work_ynh and
qualified with a yunohost-runtime.yml run after publication. Until then it is not
qualified.
Images and qualification
The release workflow builds, signs and publishes the server and render-worker images
from the v1.2.0 tag push. The release's release-source-images.json records their
verified digests. Both are signed keylessly with identity
https://github.com/lolly-tools/lolly-work/.github/workflows/release.yml@refs/tags/v1.2.0,
the identity lolly-update accepts.
The release workflow built, signed and published both images from the v1.2.0 tag (run 42686789511):
ghcr.io/lolly-tools/lolly-work-server@sha256:46bec914c2a0a5a3bc1faf0a7c86a56a319cef68f41c82ab3601c4bf7451a270
ghcr.io/lolly-tools/lolly-work-render-worker@sha256:1f2ed7b7feb01d280fe185187ec9fdd7eb777c19f86a8b060a95d49f3e18bf12
Both images build the tagged source 68fc92330e873b60e49ed0afadde9715166aec82, which passed all eight workflows of its push to main. Each is a linux/amd64 and linux/arm64 index with SBOM and provenance attestations, carries that commit as its source label, and verifies with cosign against the identity above. release-source-images.json lists each platform's manifest and config digest. SHA256SUMS covers the two YunoHost archives on lolli.li and that file.