For optional semantic checks after parsing, see the reasoner's document validation API and client orchestration.
Project-owned metadata and the settings-only project replacement protocol are described in Default observers.
This document defines the resource-management contract exposed by ResourcesService. Its public
asset API consists of resolve, retrieve, delete, list, and submit, plus the shared
KlabService.info and KlabService.query inspection operations. Asset-specific methods are
implementation helpers, not service API.
The other methods still declared by ResourcesService are operational facilities (for example,
resource contextualization, repository management, behavior parsing, and project locking). They
do not create an alternative asset CRUD surface.
In this document, asset is the generic word for any object implementing KlabAsset: projects,
namespaces, models, workflows, components, and the other managed knowledge objects are all assets.
Resource with an initial capital denotes only the concrete
org.integratedmodelling.klab.api.knowledge.Resource contract. A Resource describes an
adapter-backed dataset or computation, including its URN, version, geometry, parameters, metadata,
files, validation notifications, and history. “Resources service” is the proper name of the
service, not a claim that every asset it manages is a Resource.
The publication lifecycle in this document applies to that concrete Resource contract. It must
not be generalized to arbitrary assets without defining their own submission and review rules.
Every managed object is a KlabAsset, identified by a URN and classified by
KlabAsset.KnowledgeClass. The class is part of the request: identical text can name different
kinds of asset, and implementations must not guess when the caller can state the class.
Workspace-managed documents normally use their language URN. A project is addressed by its project
name for retrieval and deletion. Submission of a new project uses workspace/project, because the
target workspace is otherwise ambiguous. A workspace is addressed by its workspace name.
Except for public discovery, calls carry a UserScope. The scope is the authority for visibility,
ownership, and mutation. Passing null is valid only for public assets, notably worldview discovery
during service startup. Full privilege enforcement is not implemented in every provider branch yet;
those branches are explicitly listed under "Work remaining".
ResourceSet is the resolution and change-set envelope. A resolution result contains descriptors
for the requested asset and the dependency closure needed to use it, plus service locations and
notifications. A mutation returns one ResourceSet for each affected workspace. An empty result is
represented by ResourceSet.isEmpty() and may carry explanatory notifications; callers must not
interpret an empty Java collection and an error-bearing empty ResourceSet as equivalent.
| Method | HTTP endpoint | Contract |
|---|---|---|
resolve(urn, knowledgeClass, scope) |
GET /api/v1/resolve/{knowledgeClass}/{urn} |
Locate an asset and return a self-contained ResourceSet, including required dependencies. This is discovery, not object transfer. |
retrieve(urn, assetClass, scope) |
GET /api/v1/retrieve/{knowledgeClass}/{urn} |
Return the full serialized asset of the requested Java class, or null when it is not found or visible. Call resolve first when dependency loading matters. |
delete(urn, knowledgeClass, scope) |
DELETE /api/v1/delete/{knowledgeClass}/{urn} |
Remove the asset and return change sets for all affected workspaces. Deletion is idempotent only where the underlying storage operation is idempotent. |
list(assetClass, scope) |
GET /api/v1/list/{knowledgeClass} |
Return all visible assets of exactly the requested class. No filtering or descriptor conversion is implied. |
info(urn, knowledgeClass, infoClass, scope) |
GET /api/v1/info/{knowledgeClass}/{urn}?infoClass=... |
Shared KlabService operation returning a projection or descriptor. Resources owns projections for its managed asset types and delegates common service/component projections to BaseService. |
submit(asset, mode, scope) |
PUT /api/v1/submit/{knowledgeClass}/{submissionMode}/{urn} |
Add or change a typed asset and return workspace change sets. The body is the asset; the path class and URN must agree with it. |
query(parameters, knowledgeClass, infoClass, scope) |
POST /api/v1/query/{knowledgeClass}?infoClass=... |
Shared KlabService operation selecting objects and returning one requested projection per match. For resource-managed types, an empty map is equivalent to listing the class followed by projection. |
HTTP knowledge-class and submission-mode values are enum names. infoClass is the canonical Java
class name and is required by info and query. Invalid classes, incompatible projections, and
unsupported query keys are request errors; they must not silently broaden a query.
resolve answers "what must be available to use this asset?" Its result contains lightweight
descriptors and dependencies and can be merged across services. retrieve answers "give me the
asset definition" and returns one concrete object from one service. A typical remote flow resolves,
loads the returned dependency set, and then retrieves concrete assets from the services named in
the result.
The provider currently resolves resources, namespaces, ontologies, behaviors, models, projects, workspaces, worldviews, components, and service implementations. Special informational resolution identifiers are:
export-schema:<media-type>andimport-schema:<media-type>withINFORMATION;- an adapter identifier, optionally versioned, with
RESOURCE_ADAPTER; <service-call-urn>@<version>withSERVICE_IMPLEMENTATION.
Model resolution through query uses the typed convention
{"observable": <Observable>}, KnowledgeClass.MODEL, and ResourceSet.class. This preserves the
old semantic model-candidate operation while keeping it under the generic query API.
A resource URN whose node (first element) is klab, such as klab:random:..., is universal. It
does not identify stored data and is intentionally absent from every resource catalog. Its catalog
(second element) is instead the adapter identifier. Resolving it as RESOURCE returns a synthetic
RESOURCE descriptor and, when available, the embeddable COMPONENT that supplies that adapter.
A consuming Runtime must not interpret the RESOURCE descriptor as a catalog object. If the adapter
is missing and the RESOURCE dependency set did not carry its component, Runtime resolves the
adapter identifier separately as RESOURCE_ADAPTER through the merged Resources client. The
result identifies the providing COMPONENT, which Runtime installs before verifying the
embeddable adapter. Retrieval is deferred until the compiled dataflow uses the URN; at that point
any Resources service with the adapter can synthesize the Resource. Ordinary non-klab resource
URNs retain the owner-specific resolve-then-retrieve contract. Adapter descriptor retrieval and
queries use RESOURCE_ADAPTER; the former INFORMATION projection is not part of this contract.
The same adapter lookup is also a compatibility fallback when the merged RESOURCE lookup is empty. If it installs an embeddable adapter, Runtime synthesizes the universal RESOURCE descriptor locally and continues resolution. This lets a newer Runtime consume an adapter advertised by a Resources service that does not yet implement synthetic universal-RESOURCE resolution.
A universal adapter need not be embeddable. For a non-embeddable adapter, the RESOURCE descriptor's service ID identifies the Resources service that will synthesize and execute it. Runtime does not request or install that component; dataflow compilation retains a remote adapter descriptor and execution uses the Resources contextualization API and Avro data transport. Thus universal-resource resolution supports both local embedded execution and remote hosted execution.
WorkspaceManager is the source of full workspace-managed assets. It retrieves and lists
workspaces, projects, namespaces, ontologies, observation-strategy documents, behaviors, models,
and symbol definitions. The provider additionally retrieves resources, concepts, observables, and
the served worldview.
Worldview retrieval currently supplies a container from one provider; it does not establish a complete protocol for assembling certified higher-tier contributions from several services. The intended production rule also restricts authority plug-ins to the authorized services supplying the loaded worldview. Ordinary Resources providers must not acquire that role through general component distribution. These source restrictions are not yet enforced. See worldview composition and authority provenance for the proposed distributed contract and full-local development requirements.
list has no search semantics. Clients that need matching, sorting, or a different representation
must use query. In a multi-service scope, ResourcesMerger snapshots all resource services,
including Resources clients advertised in a service-side scope even when they are temporarily
absent from its live status-filtered typed projection. It de-duplicates the snapshot by service ID
(or URL when no ID is known), excludes itself, queries the services concurrently, tolerates an
individual failure, and returns distinct results in service order. This prevents a cached merger
from silently becoming local-only while the advertised federation still contains a remote
provider. retrieve, writes, and operational calls go to the primary service because their results
cannot be combined safely.
info and query belong to KlabService, so every service exposes the same transport and typed
projection contract. BaseService supplies common inspection of installed components, adapters,
service implementations, service capabilities/status, and portable DomainObject descriptors.
ResourcesProvider retains exclusive knowledge of resource-managed asset classes and delegates a
request to BaseService when the requested projection belongs to that common contract.
If infoClass is compatible with the asset itself, info is equivalent to retrieve. String
returns the matched URN, which lets query callers request identifiers without a separate
listResourceUrns endpoint. Current provider projections also include:
ResourceInfofor catalog and authorization metadata;ResourceTransport.Schema.LanguageDescriptorfor a language identifier withINFORMATION.
AdapterDescriptor, component descriptors, and service-implementation descriptors are supplied by
the common BaseService component-registry implementation and are therefore available from any
service that has loaded the corresponding component.
Additional DomainObject projections should describe a stable schema suitable for API discovery.
Unsupported projections fail explicitly. Model Coverage is reserved but not computed yet.
The parameter map belongs to the selected asset class. Current general parameters are:
urn: a Java regular expression matched against the complete URN;query: an alias for the general textual/URN pattern;limitormaxResults: cap aResourceInfocatalog query to 1–100 results;includePublishedLocal: include retained local sources that have already been published; the default isfalseon a local Resources service;observable: the typed model-candidate query described above.
An empty map selects all visible assets of the class. If the requested infoClass is the asset
class, the result is equivalent to list. Otherwise each selected asset is transformed through
info. ResourceInfo queries use the resource catalog. Any unsupported key is rejected with a
TO BE IMPLEMENTED error rather than ignored.
Submission modes are:
ADD: create only; an existing asset is not overwritten;REPLACE: replace the current representation;UPDATE: update while retaining history when storage supports it;MERGE: merge submitted content into the existing asset.CREATE_OR_UPDATE: create an absent asset or save a newer version of an existing one;PUBLISH: submit a concreteResourceto a remote authoritative service for tier-1 review.
The current provider can ingest Resource, create Workspace, create Project, and replace/update
file-backed k.IM documents carrying both projectName and sourceCode. A new project asset must use
the workspace/project URN. Resource updates preserve the previous current representation in the
embedded history and require the submitted version to be newer. k.Actors document mutation,
project/workspace update, and merge semantics remain pending and return explicit notifications.
ProjectMaterial is a binary-safe KlabAsset with knowledge class and project resource type
ADDITIONAL_MATERIAL. It contains projectName, path (canonical project-relative path),
content (byte[], Base64 in JSON), and service/metadata fields. Its URN is project/path.
It is not a KlabDocument and is never parsed as k.IM or k.Actors source.
Use the existing generic submit, retrieve and delete methods. The client uses these
query-coordinate routes so relative paths never depend on encoded slashes in a path segment:
PUT /api/v1/submit/ADDITIONAL_MATERIAL/{mode}?urn=project/review/notes.txtGET /api/v1/retrieve/ADDITIONAL_MATERIAL?urn=project/review/notes.txtDELETE /api/v1/delete/ADDITIONAL_MATERIAL?urn=project/review/notes.txt
The submitted body and query URN must agree. Modes are ADD (requires absence), UPDATE (requires
existence), and CREATE_OR_UPDATE; REPLACE, MERGE and PUBLISH are rejected. The filename, including
extension, is part of path; bytes are preserved without transcoding. core.project.write_text
is the UTF-8 convenience operation.
Creation/update require service UPDATE_METADATA and access to the containing project (ownership or its existing user/group rights). Reading requires READ or UPDATE_METADATA. Deletion requires DELETE. Administrators retain their override. A user with UPDATE_METADATA alone cannot perform document CRUD, replace project settings, or delete material. Project API permission descriptors report these effective operation grants; actors enforce the same rules as the service.
Only service-owned FileProjectStorage is writable. Parent directories are created automatically;
paths must already be canonical, use /, and remain within the project. Absolute paths, traversal,
platform aliases, symlink traversal, language-document paths (including case variants), Git control
files, META-INF, and managed resources paths are rejected even if the target does not yet exist.
Thus additional material cannot replace canonical documents or bypass their permissions. Payloads
are limited to 32 MiB. Ignored or conflicted Git paths fail explicitly.
Mutations stage the one affected path in Git without committing or pushing; unrelated staged changes remain intact. An unlocked project accepts metadata contributions without requiring UPDATE. A project locked by another user rejects mutations. Workflow checkpoints and project Git writes are separate persistence operations; retrying a workflow does not undo previous project effects.
A new Resource is first created on a local Resources service. Its adapter contract, required
parameters, geometry, identity, version, metadata, and local-file integrity are validated before a
normal save is enabled. Creation establishes a ResourceInfo catalog record with owner-scoped
rights, Stage.STAGING, and review status 0 (tier 0).
The editor distinguishes two subsequent mutations:
- Update temporary data uses
REPLACE. It keeps the same version and is accepted only by a local service or for a tier-0 record. It is intended for incomplete staging metadata and still requires a valid resource overview. - Save new version uses
UPDATE. Full validation must pass, the version must be newer than the current version, and the former current representation is appended toResource.history. Historical versions do not appear as separate browser results.
Permissions do not live in the serialized Resource. ResourceInfo.rights is independently
persisted in ResourcesKBox, and the editor updates it through its dedicated permissions action.
Saving resource content does not implicitly save permission edits.
Project settings use a different, combined save contract. WorkspaceEditor loads project-owned
metadata and the catalog rights into one draft. A settings-only project REPLACE submission
stores metadata in META-INF/manifest.json and an optional ProjectSettings.permissions update
in the project's ResourcesKBox record. Null permissions leave the catalog unchanged; empty text
means owner-only access. Owner and service grants are preserved. The requester needs service UPDATE
plus project access (or service administration), and must own the project lock. The normal project
setRights route has the same checks. Project ResourceInfo.permissions exposes effective UPDATE
access for the settings UI. See the settings contract
for error recovery and transport details.
Publication is a separate tab, not a variant of local Save or Update. It is available only when all of these conditions hold:
- the source service is local and the local
Resourceis no longer an unsaved draft; - client-side validation has no errors;
- at least one available non-local Resources service grants
CRUDOperation.CREATEto the currentUserScope; - the user explicitly selects and confirms the target, acknowledging that local editing becomes restricted.
Target discovery and submission run off the JavaFX application thread. The publication action is a
WaitButton, so a long adapter validation or remote ingest exposes waiting/success/failure state
without blocking the UI.
The caller is an editor when WorkflowParticipant.from(UserScope) contains WorkflowRole.EDITOR
(workflow administrators also qualify). Otherwise the publication tab requires an intended editor
identity and sends it as klab.publication.intendedEditor metadata. The remote endpoint rejects a
non-editor submission without that metadata even if a non-IDE client bypasses the UI.
The client submits the complete Resource to
PUT /api/v1/submit/RESOURCE/PUBLISH/{urn}. PUBLISH is invalid for any other knowledge class. The
target enforces CREATE permission and rejects the request when:
- the target is local;
- any version of the URN already exists;
- incoming validation notifications contain an error;
- mandatory adapter parameters are missing;
- the target adapter's local-import validator rejects the resource; or
- a non-editor submission has no intended editor metadata.
The adapter validator or metadata analysis may replace the catalog and namespace components. Once that processing succeeds, the service replaces the first URN component with its own sanitized service name and returns that final authoritative URN. A duplicate check is repeated against the final URN, so analysis cannot accidentally overwrite an existing resource.
Successful ingest creates exactly one current resource keyed by that final URN. Its remote ResourceInfo is
set to Stage.REVIEWING and review status 1 (tier 1, “under review”). The configured
publicationReviewWorkflow defaults to asset-review. For an editor submission, the service tries
to start that workflow automatically in its editing state. A missing or unusable workflow does
not roll back the accepted resource; it is reported as a warning so review can be started manually.
Setting the configuration value to blank disables automatic workflow creation.
Only after remote acceptance does the client call the source service's markPublished API. The
source must itself be local, and only the resource owner or a service administrator may change the
record. The endpoint is PUT /resourceInfo/{urn}/publication/{serviceId} and its request
body carries the final authoritativeResourceUrn. The local ResourceInfo then stores:
published = true;authoritativeServiceIdequal to the accepting remote service ID; andauthoritativeResourceUrnequal to the final URN returned after remote validation and analysis;publicationTimestampfor the successful handoff.
This is a cross-service two-step operation, not a distributed transaction. If the remote accepts the resource but the local catalog update fails, the editor reports that exact partial outcome; the remote copy is authoritative and the local record must be reconciled rather than submitting a duplicate blindly.
Normal local ResourceInfo search omits records whose local source has published = true.
includePublishedLocal=true opts into those records through the generic query API; the resource
browser exposes the same option as Show published local resources. The filter applies only to a
local service, so it never hides the authoritative remote record.
When an opted-in published local source is opened, its resource fields, Save, Update, and Delete actions are read-only by default. Edit published local copy is shown beside the local action buttons. Checking it opens an explicit warning that local changes do not update the authoritative server; only confirmation enables editing. Permissions retain their independent authorization and update action.
The authoritative service ID is retained even when that service is unavailable. Re-publication is
allowed when the recorded authority is unavailable. If it is available, only a user with
CRUDOperation.ADMINISTER may choose a different target, and the editor warns that normal
operations belong on the authoritative service.
The recorded authoritative service is not offered again while it still returns the recorded
authoritativeResourceUrn. It becomes
eligible only after that remote resource has been deleted. Independently of UI discovery, every
remote PUBLISH request rejects an existing URN, including a different version, so concurrent or
stale clients cannot turn publication into an update.
Deletion is selected by knowledge class and delegates to the existing storage-specific helpers. Document, project, workspace, and resource removal return workspace change sets. The remote client currently cannot deserialize the response body of its generic HTTP DELETE helper, so it returns an empty Java list after a successful request; this transport limitation must be fixed before remote callers can react to deletion change sets.
| Old operation | Generic operation |
|---|---|
resolveModel, resolveResource, adapter/service/schema resolvers |
resolve(urn, knowledgeClass, scope) |
resolveModels(observable, scope) |
query(Map.of("observable", observable), MODEL, ResourceSet.class, scope) and merge returned sets |
retrieveNamespace, retrieveOntology, retrieveBehavior, retrieveProject, retrieveWorkspace, retrieveResource |
retrieve(urn, AssetClass.class, scope) |
retrieveWorldview |
list(Worldview.class, scope) or retrieve(advertisedUrn, Worldview.class, scope) |
listWorkspaces, listProjects |
list(Workspace.class, scope), list(Project.class, scope) |
listResourceUrns |
query(Map.of(), RESOURCE, String.class, scope) |
resourceInfo, retrieveAdapterInfo, modelGeometry |
info(urn, knowledgeClass, Projection.class, scope) |
queryResources |
query(parameters, knowledgeClass, ResourceInfo.class, scope) |
| create/update/register methods | construct the typed asset and call submit(asset, mode, scope) |
| specialized delete methods | delete(urn, knowledgeClass, scope) |
Specialized methods that still encapsulate useful storage logic remain concrete helpers inside
ResourcesProvider; they are not overrides and are not callable through ResourcesService.
Client implementations contain only generic CRUD calls. Legacy controller routes and constants
remain temporarily for server compatibility and should be removed after external clients migrate.
The interface retains operations whose contract is not asset CRUD: resource contextualization and data production, parsing standalone documents from an arbitrary URL, repository operations, importing through transport schemas, publishing observations, and project locking. These operate on execution, transport, or transaction state and should not be forced into CRUD solely to reduce method count.
parseAsset(URL, Class<T>, UserScope) accepts exactly four standalone document contracts:
KActorsBehavior, KimOntology, KimNamespace, and KimObservationStrategyDocument. It parses
without registering the result in a workspace. Remote clients upload the URL contents together with
the requested document class, so file URLs do not need to be accessible from the service host.
The following gaps are deliberate and must remain visible until supporting contracts exist:
- enforce privileges consistently in every resolve, retrieve, query, submit, and delete branch;
- implement model coverage for
info(..., MODEL, Coverage.class, ...); - implement resource composition and caching for multi-URN retrieval;
- implement import-schema resolution and geometry-aware schema selection in the generic identifier;
- implement project/workspace update and
MERGEsubmission; - support k.Actors document mutation in
WorkspaceManager; - make workspace deletion atomic and report complete change sets;
- return DELETE response bodies from
ResourcesClient; - enrich the minimal common
DomainObjectprojection with stable type-specific schemas; - replace public startup calls with an explicit public-scope contract instead of relying on a null scope;
- migrate and then remove legacy resource controller mappings and
ServicesAPIconstants.
The sibling klab-ide audit covered its resource-service call sites as well. Its standalone
k.Actors parsing in AgentView and BehaviorEditor now calls
parseAsset(url, KActorsBehavior.class, scope). Its workspace and resource views use the generic
retrieve, query, info, and typed submit operations. Calls from KlabIDEController into the
modeler's own create/update methods are not ResourcesService calls and remain valid; the modeler
implements those workflows through the generic service API where supporting payloads exist.
Workspaces are virtual containers. Their metadata, permissions, creator (owner) and project
membership live in the workspace ResourceInfo in ResourcesKBox; no workspace settings file
is created. New workspaces record the authenticated creator. Existing workspaces without an
owner remain administrable; membership in their access list does not imply ownership.
Submit a Workspace with SubmissionMode.REPLACE to replace metadata and/or privileges. A null
metadata or privileges field leaves that part unchanged; empty metadata clears metadata and
empty privileges restrict access to owner/admin (existing service grants are retained). Both
settings are persisted in one catalog update. Submitted ownership and project membership are
ignored. Only the stored owner or a service administrator may save, including through generic
rights and resource-info update routes. Project locks and project edit permissions are irrelevant.
Workspace info exposes READ to authorized viewers and UPDATE/ADMINISTER to the owner/admin. Other viewers can audit settings without editing them. Reads honor explicit exclusions, while owner/admin access is retained even when not listed in the ACL. Catalog saves refresh cached workspace metadata and privileges without rebuilding project membership. The IDE settings tab uses this contract and retains drafts after failed saves.
Project settings saves preserve unrelated manifest fields. ProjectSettings.definedWorldview is
an optional admin-only manifest update (null unchanged, blank removes it); all saves still require
a project lock and edit access. Observer edits require a nonblank effective definedWorldview.
Legacy META-INF/project.json metadata is migrated on save and that file is removed. Failure
restores both original files. No permissions are migrated into the manifest.
Under Git, settings remain working-tree changes for the existing repository Save/Publish actions, which stage additions, modifications and deletions. The settings save leaves index and HEAD intact, rejects conflicts on either settings path and ignored manifest paths, and returns fresh repository state. Manifest and legacy settings paths are recognized during repository change processing so pull/discard operations refresh project settings and invalidate cached worldview metadata.
Project settings submissions carry workspace/project in the JSON body, but use the project
identifier alone in the existing submit route's {urn} segment. Encoding the coordinate slash
as %2F causes Tomcat to reject the request with HTTP 400 before controller authorization or
observer validation. The project controller obtains the coordinates from the body.
ResourcesClient.submit uses strict PUT handling: HTTP/transport failures throw an error with
the HTTP status instead of becoming an empty collection. The IDE shows service rejection text
in the settings tab as well as forwarding notifications. A successful empty response is reported
as a missing save result, without suggesting that notifications exist.
ProjectSettingsTransportTest covers the mounted URL, complete body coordinates, observer text,
and HTTP 400/403/500 propagation. The encoded-slash rejection was reproduced against the local
Resources service; this does not constitute an authenticated end-to-end settings save.