The platform underneath
Console API v2, the build file format and the platform behaviours worth knowing about.
This page is about the platform rather than the tool: what the Console API looks like, what
a build file is made of, and how the platform behaves in the cases that surprise you the
first time. The platform’s own documentation does not cover these corners, and elemctl had
to learn them by running into them.
You do not need this page to use the tool. It is here for the times when something behaves oddly and you want to know what happens underneath. Or when you are writing a client of your own.
Console API v2 contract
Common prefix: {base}/console/api/v2. Request and response bodies are JSON, apart from the build upload. Field names are in kebab-case.
The newer console reference documents Console API 2.1 as the main version, with the prefix {base}/console/api/v2.1, and keeps 2.0 beside it, working. elemctl stays on 2.0 and takes 2.1 only for what 2.0 has not got: the extensions applied to an application. A console that has no handler for a path answers 401 rather than 404, with the text Handler of HTTP request "[<method>] <path>" in application "console" not found., and that is how a server older than a method refuses it. A renewed token gets the same answer, so elemctl reads the text before the token is renewed and names the answer as a method the server does not know rather than as a failed sign-in.
Applications
GET /applications– list. Thenamequery parameter exists, but the platform ignores it and returns the full list. That was verified against a live instance, so filtering by name has to happen on the client.GET /applications/{id}– card. Significant response fields:id,status,uri(address of the running application),error(error text, if any),technology-version,date-updated,display-name,publication-context,source(an object with source information, containing among other thingsproject-version– the version of the applied build).POST /applications– create. Body:source– the object{"type": "repository"}plus exactly one of the keys:project-version-id(id of the source build) orimage-id(project id);display-name,publication-context– publication name and path;development-mode– boolean, whether to create a development environment;- optional
space-id,technology-version.
DELETE /applications/{id}– delete.PUT /applications/{id}/status/start– start.PUT /applications/{id}/status/stop– stop.POST /applications/{id}/actions/debug– data for a debug session (ApplicationDebugInfo:{"debug-token": ..., "debug-address": ...}). The request body is empty; requires debugging enabled on the server (config/debug.ymlenabled: true).POST /applications/{id}/project/update– apply a build to the application. Body:{"source": {"type": "repository", "image-id": "<build id>"}}or{"source": {"type": "repository", "project-id": "<id>", "assembly-version": "<version>"}}(assembly-version is optional). A build of an extension goes to the same method and is applied beside the build of the application:source.project-version-idof the card goes on naming the build of the application, and the extension shows up inextension-projectsofGET /v2.1/applications/{id}/project, with the version of the build inassembly-version.elemctlverifies such an apply by that list.POST /applications/{id}/project/export– the build the application runs, documented under 2.0 and 2.1 alike. No body; the answer is the archive itself,application/octet-streamwith noContent-Disposition, holding the files of the build packed anew. The console takes it from the application server, and the extensions applied to the application are not in it. During an update, and after a failed one, the answer is the build uploaded for the update. A build uploaded into a project comes back with the number the server gave it in the manifest. The handler of 2.1 also takes an undocumentedextension-idquery parameter and then answers with the build of that extension.elemctl apps exportsaves the archive under{Name} {Version}.xasm.POST /applications/{id}/dumps– create a dump. Body:include-users,include-binary-data(booleans),description(string).GET /applications/{id}/dumps/{dumpId}– dump status.GET /applications/{id}/users– the users connected to the application, each{user-list-id, user-id, presentation, is-admin, token-access-enabled}. The users of the control panel are among them: the platform connects them itself and names such a user by the login.token-access-enabledis the access of the user to the HTTP services of the application by a token; without it a call of a service with the user’s token is refused with a 500 “Token access is denied”.PUT /applications/{id}/users/change-token-access– switch that access. Body:{"user-list-id", "user-id", "enable-access"}, all three required; the answer is the connection as it became. A user who is not connected to the application gets a 500 “Can’t change user token access” that does not say why. The method is documented under v2 and v2.1 alike.elemctl apps token-accessfinds the connection first and reads the flag back after a switch, andelemctl apps usersprints the whole list.GET /me– the user the credentials belong to:id,login,presentation,user-list-id.GET /v2.1/applications/{id}/project– the projects the application runs:application-project, and the extensions applied to it inextension-projects, each withid,project-id,assembly-id,enabled,order,vendor-name,project-name,project-presentation,project-versionandassembly-version.idis the id of the extension on the application server: theИдof the extension’sПроект.yaml, theconfiguration-idits upload answered with. The console fillsassembly-idof an extension with the id of its project, so that field names no build. The same path of 2.0 answers with the application project alone.POST /v2.1/applications/{id}/project/{ExtensionId}/export– the build of an applied extension. No body; the answer is the archive itself,application/octet-streamwith noContent-Disposition, holding the files the build was uploaded with. The segment is theidof the extension, in either case of letters. The reference calls the parameter the id of the extension project, but theproject-idgets the same bare 500 “Unable to export extension … from application …” as an unknown value. A stopped application exports all the same. 2.0 has no such method.elemctl apps export-extensionlooks the extension up by any of its names first and saves the archive under{Name} {Version}.xasm.
Application statuses: stable Running, Stopped, Error; transitional Starting, Stopping, Initializing, Updating, Frozen, Creating, Deleting. During transitions the status field may also be empty. The reference describes status as a plain string and lists no values, so this list is what live checks have met. Deleting lasts while the DeleteApplication task runs, and then the application stays in the list under Deleted. UNKNOWN, in capitals, was met on an application whose database files were gone: it could be neither started nor deleted, because a deletion begins with an export of the data. The console gives that status to a server state of the application it has no name for, so nothing says it is final, and the client counts it as neither stable nor transitional: a wait for a status puts up with UNKNOWN for a minute in a row, then stops and names it rather than running out its whole timeout.
Technology version
- Reading – from the
technology-versionfield of the application card (a dedicated read endpoint is not present in all platform versions – do not use it). - Update:
POST /tasks/group-tasks/update-applications-technology, body{"technology-version": "<version>", "applications": ["<app-id>"]}. Returns a group task; its status –GET /tasks/group-tasks/{taskId}.
Spaces and projects
GET /spaces– list of spaces.GET /projects– list of projects;GET /projects/{id}– card;DELETE /projects/{id}– delete. The kind of a project is inproject-kind:Application,LibraryorExtension, andGroupfor the group of projects the list carries beside its members.
Project builds (assemblies)
- Uploading a build file – a binary POST (Content-Type
application/octet-stream, body – the file bytes):POST /projects/{id}/assemblies– add a build to an existing project;POST /projects– create a new project from a build, or add the build to the project that carries theИдof itsПроект.yaml;POST /spaces/{space-id}/projects– the same, with a new project created in that space. The space of an upload into a project goes as the optionalspace-idquery parameter, spelled the way the reference spells it. A live check found the server reading it: an id that is not a UUID is refused with a 400 and an unknown space with a 404, while the PascalCaseSpaceIdthe client used to send went through unread.POST /projectsdocuments no space parameter, and the server reads neither spelling there: a new project uploaded withspace-id=not-a-uuidwas created all the same. So the space of a new project goes into the path ofPOST /spaces/{space-id}/projects, which answers an unknown space with a 404 and adds a build whoseИдalready has a project to that project, the wayPOST /projectsdoes. The commit the build was made from goes ascommit-id, a query parameter the reference documents forPOST /projects/{id}/assemblies, and the server puts it on the build card. The reference listsbranch-name,commit-messageandmodifiedfor the same method too, and a live check found the server taking them and showing none on the card: itsbranch-namenames a branch of group development, not of git, andmodified=1is refused with a 500. The PascalCaseCommitId,BranchNameandCommitMessageare not parameters of the method at all, and the server ignores them.POST /projectsdocuments no commit parameter, so a build that creates its project carries no commit.elemctlsends the commit alone and keeps the branch, the state of the tree and the directory of the sources in a local registry of uploads. The two uploads treat the version of the archive differently. An upload into a project,POST /projects/{id}/assemblies, does not read it at all: the server takes the base from theВерсияof the project descriptor and gives the build the highest number it has ever given in that base plus one. Checked live: an archive of1.0.0-i1became1.0.0-1, the next one,1.0.0-10001, became1.0.0-2, a1.0.0-2already in the project became1.0.0-3, and1.0.1-7became1.0.0-502in a project whose descriptor says1.0.0. A deleted build keeps its number: the build list no longer shows it, and the server counts on from it all the same. With1.0.0-4deleted from the top of the list, the next upload became1.0.0-5, and with1.0.0-50and1.0.0-51deleted, it became1.0.0-52while the list ended at1.0.0-8; a build deleted from the middle of the list changes nothing. The count is nowhere to be read: the project card does not carry it, the build list does not show a deleted build, and the card of a deleted build answers with a 404. That upload answers with the card of the build, and itsassembly-versionis the number the server handed out. An upload without a project id,POST /projects, keeps the version of the archive as it is, a suffix that is not a number included, and answers a version the project already has with a 409 (below). The number of a deleted build is free for it again, and taking it does not wind the count back: an archive of1.0.0-2went in under that number after the earlier1.0.0-2had been deleted, and the next upload into the project became1.0.0-8, not1.0.0-3. So the suffix of a version, the number of a CI run for instance, survives the second way and never the first.elemctl deploynames a version the server will not keep before the build and a build the server numbered otherwise right after the upload. Without an explicit version it counts the number on from the higher of the build list and the local registry of uploads, which remembers the numbers the uploads of this machine got, so a build this machine uploaded and deleted does not throw the count back. A build uploaded from elsewhere and deleted since is in neither of them, and the line after the upload then says that the number is the server’s. The response carries the id of the created build in one of the fieldsimage-id,assembly-idorid, checked in that order. Next to it sits anartifactobject naming the project the build landed in:artifact-idis the project id and opens as a project card,configuration-idis theИдofПроект.yaml, andnameis the project presentation. The console shows a project under the presentation of the build uploaded last, theПредставлениеof itsПроект.yaml, not under the manifestName: builds of oneИдuploaded with a newNameand a new presentation each left the project card named after the presentation every time. So a build uploaded into a project under a different name silently renames that project, and uploading a build with the project’s own name puts the name back.elemctlwarns about such a mismatch before uploading. An upload without a project id renames a project the same way when itsИдleads to a project that has another name. Nothing can be compared before such an upload: the server chooses the project, and no method finds a project by itsИд. Sobuilds uploadreads the project list before the upload and holds theartifactof the answer against it: it names the project the build went into, says whether the upload created it, and warns when the upload renamed it.
- A project is identified by the
ИдofПроект.yaml, theconfiguration-idof the answer.POST /projectstherefore does not always create a project: when a live project carries thatИд, in the space of the upload first and then in any other, the build is simply added to it, whatever the vendor and the name of the manifest, and that project comes back inartifact-id. TheVendor+Namepair is a constraint rather than the key: one project of a space per pair. A live check took the two apart: builds ofacme/crm,acme/crm-nextandglobex/crmwith oneИдall went to one project, which took the name of each in turn, and a build ofacme/crmwith a freshИдgot a 409. There are two ways to hit a 409ALREADY_EXISTS. The first is uploading a version that is already there, answered with “Версия сборки … уже присутствует в группе проекта”. The second is a freshИдwith a vendor and a name another project of the space already holds, answered with “Сборка с именем поставщика … уже зарегистрирована в другом проекте”; the project the console started for thatИдgoes with the refusal. A second, throwaway project for the same sources takes a freshИдand a fresh pair together. GET /projects/{id}/assemblies– list of builds. Each element containsassembly-version, a string like1.0-42, and an id inidorimage-id. The response is either an array or an object with the list in theitemsorassembliesfield. The method has no pages and reports no total.limit,size,pageSize,count,top,maxResults,page,pageNumber,offset,skip,fromandstartare all ignored: the answers to every one of them match byte for byte, and neither the headers nor the body carry a counter or a cursor. Verified by live calls on two installations. The platform deletes builds nobody uses, and age has nothing to do with it: the vendor’s help calls this automatic deletion of unused builds. The console does it at one moment only, when an application finishes applying a build: a background job then goes over the builds of that application’s project with the same base version as the build it now runs. A release build stays, and so do a protected build, the project’s default build, a build a live application of the project runs or has had uploaded into it, and the build with the highest number of that base. The rest go, ten at most per apply. A build uploaded without a project id (POST /projects) is protected on arrival, like the build the project’s repository was created from, so no apply removes it, while a build uploaded into a project by its id (POST /projects/{id}/assemblies) is not protected. A library build no remaining build depends on goes with the builds of its group. The builds of an extension project are outside the housekeeping altogether. An extension is applied by the same update of the application, and the job that follows starts from the build the card of the application names: it goes over the application’s own project and takes the unused builds of that base there, while the extension project keeps every build, the ones no application runs included, until they are deleted by hand or go with the project. A live check agreed with the source: the application ran1.0.0-1of its project,1.0.0-2and1.0.0-3had been uploaded into that project by id, and the extension project held1.0.0-1to1.0.0-4. Applying the extension build1.0.0-2took1.0.0-2of the application project and nothing of the extension project, and applying1.0.0-4after it left the extension builds1.0.0-2and1.0.0-3where they were. So a project nobody applies anything to keeps every build, and a build uploaded for later survives the apply of another one only while its number is the highest. The source of the console in the server distribution says so, and a live check on a project of nine builds agreed with every line of it. Creating an application deleted nothing in two minutes. Applying1.0.0-3took1.0.0-2,1.0.0-4and1.0.0-11away before the apply returned, and left1.0.0-1and1.0.0-10, both uploaded without a project id,1.0.0-3,1.0.0-12, the highest, and both builds of2.0.0. Applying1.0.0-12took1.0.0-3. Applying2.0.0-1took nothing,1.0.0-12included, since that apply looked at2.0.0alone. Seen earlier: seventeen builds of one day’s series were made and one survived, the one the application runs, while a build from two months earlier is still listed: it is the project’s first, and the upload that creates a project is an upload without a project id. So a listing is not the project’s history and not a page of it. It is what survived, and the client must say so.GET /projects/{id}/assemblies/{version}– build card,DELETE .../{version}– delete. The last segment is the version, the way the method names it:assembly-versionorproject-version, a string like1.0-42. The id of the card does not go there: a UUID is answered with a 404 “Assembly with versionnot found”. Checked live on two installations of different ages, both behave this way. An id is still an address a caller holds: the build list prints it, and an upload answers with one. So elemctlaccepts both forms and looks the value up in the build list to get the version. When the address is refused with a 400 or a 404, it tries the id as the segment as well, for an installation that wants that form. The version of a build uploaded into a project is the number the server gave it, not the one of its archive. Deleting a build is rejected with a 500 while an application created from it is still alive. Once that application has really disappeared, the very same request succeeds. A build that only took part in an apply, even a rolled-back one, deletes without a fuss.
Build versions are compared by the numeric suffix after the last hyphen: 1.0-10 is newer than 1.0-9. Lexicographic comparison gives the wrong order.
Development environment branches
GET /branches– list; optional queriesproject-id,name.GET /branches/{id}– card. Fields:name,kind,project,application,source-branch,deletion-mark,version-stamp.POST /branches– create. Body:name,kind: "development",project: {"id": "<id>"}, optionallyapplication: {"id": "<id>"}.PUT /branches/{id}– modify. The platform uses optimistic locking, so read the card first, then send a body assembled from the current values. Those arename,kind,deletion-markandversion-stamp, which has to come back exactly as it was. Collapsesource-branchandapplicationto{"id": ...}, or to{"name": ...}when there is no id. To rebind to an application, replaceapplicationwith{"id": "<new app-id>"}.- Branch changes are accepted by that same
PUT /branches/{id}with an additional body keywrite-parameters: {"merge": true}. DELETE /branches/{id}– delete the branch.
The tool works only with the documented Console API v2. It neither uses nor describes the internal, undocumented APIs of the platform console.
User lists
A user list holds either the users of an application or the users of the control panel. An application has a list of its own, named after it, and the application card points at it with default-user-list. The control panel has one list per installation.
GET /user-lists– the list:id,presentation,space-id. There is no server-side name filter.GET /user-lists/{id}– the full card.POST /user-listscreates one and wants the whole card: an incomplete body is answered with a 500 and the misleading text “Failed to parse json”.DELETE /user-lists/{id}removes it.GET|PUT /user-lists/{id}/settings/self-registration–{enabled, phone-required, email-required}, the control panel’s “allow users to register themselves”.GET|POST /user-lists/{id}/settings/account-services-settings,PUT|DELETE .../{account-service-id}– the account services. An entry is{account-service-id, account-service-type, local-id, enabled, create-user-on-auth, additional-settings}. The typeLocalauthenticates by a password; the rest are external:OIDC,Cas,ActiveDirectory,Esia. Both writes want the whole entry.GET|POST|DELETE /applications/{id}/userlists– the ids of the lists connected to an application. Note the spelling:userlistshere,user-listsat the top level. The link carries no settings of its own.GET /user-lists/{id}/users– the users of the list,idandloginamong their fields. An entry carries the access tokens of the user too, secrets included, so such an answer does not belong in a log as it is.
Worth knowing before you build on this:
- the rules for parsing an account service response are accepted under the key
userPropertiesCalculationRules. Those rules arepresentation-rule,email-rule,phone-ruleandresponse-kind. The reference’s own schema calls the fieldcalculation-rules, and that spelling is answered with a 400. A GET never returns the rules: the setting is write-only, and an API client cannot confirm it applied; - the composition of an application’s authentication forms is not in the API at all, and neither is the connection setting “users of the list are connected automatically on sign-in”. Those stay in the control panel;
- a GET of an account service returns the
client_secretof an OIDC client in cleartext, so such answers do not belong in logs and reports as they are; - an unknown path of the Console API is answered with a 401 carrying “Handler of HTTP request … not found”, not a 404. When probing for the surface, that is the sign that a method does not exist.
Application tasks
GET /tasks/application-tasks – list of tasks for all applications. There is no server-side filter, so filter on the client. Task fields: id, application-id, status (including Error, Failed), operation-type, error-message, start-date (ISO 8601, may end with Z).
Build file format (.xasm / .xlib)
A build file is a ZIP archive (deflate):
-
at the root,
Assembly.yaml– the manifest, flat key-value pairs:ManifestVersion: 1.0 | 1.1 ProjectKind: Application | Library | Extension Vendor: <vendor> Name: <project name> Version: <version, e.g. 1.0-42> Created: <UTC, format YYYY.MM.DD HH:MM:SS> BranchName: <git branch name> CommitId: <commit hash>For a library (
ProjectKind: Library), aRelease:line (empty value) is added at the end; the file extension is.xlib, for an application and an extension –.xasm.An extension (
ProjectKind: Extension) takesManifestVersion: 1.1, every other kind1.0. The server picks the reader of the manifest by its version, and the reader of 1.0 knowsApplicationandLibraryalone: an extension under 1.0 is refused on apply with “Unknown project kind: "Extension"”. The console writes the same pair for an extension. -
then the project files at paths
{vendor}/{name}/...relative to the repository root. The project directory must follow the scheme{repo}/{vendor}/{name}/Проект.yaml. Path separators in the archive are forward slashes, on Windows too.
Build file name: {Имя} {Version}.xasm (with a space).
Project metadata comes from Проект.yaml. It is YAML, and parsing the flat top-level “key: value” pairs is enough; skip the nested indented lines. Bilingual sources are a capability the platform declares, and a descriptor written with English keys deploys fine, so every key is read in both spellings: Имя/Name, Поставщик/Vendor, Версия/Version (base, e.g. 1.0) and ВидПроекта/ProjectKind. The value Библиотека or Library means a library, Расширение or Extension an extension; anything else means an application. When both spellings of a key are present, the Russian one wins. The service file names are bilingual too: the platform converter accepts Project.yaml and Проект.yaml, Subsystem.yaml and Подсистема.yaml. An English descriptor also carries English enumeration values, such as VisibilityScope: Global.
When the build version is not set explicitly, it is built as {base version}-{N+1}, where N is the counter from the version of the project’s latest build of the same base version. deploy takes N as the higher of the latest build in the list and the highest number the local registry of uploads remembers for the project in that base: the server counts on from the highest number it has ever given, deleted builds included. A project bumped to a new base version starts from -1 again, whatever counters the old base reached. With no last build of that base, the suffix comes from the CI run number in the environment: the first numeric value of CI_PIPELINE_IID, GITHUB_RUN_NUMBER, BUILD_NUMBER, in that order. Otherwise a clean CI checkout would produce -1 every time. With no CI number either, the version is {base version}-1.
Git metadata, meaning the commit hash and the branch name, comes from the git repository that contains the project directory. When git is unavailable, the fields stay empty.
File selection for the archive:
- only these extensions are included:
.yaml .xbsl .xbql .md .txt .json(sources),.png .svg .jpg .jpeg .gif .webp .ico(images),.css .htm .html .js .woff .woff2 .ttf .eot(web resources); - the extension filter does not apply inside a
Ресурсыdirectory, at any level and in subdirectories too: by the platform’s own documentation a resource is an arbitrary file, so everything there goes in; - the description files of a SOAP service client are included wherever they lie:
<Client>.Wsdl.<n>and<Client>.Xsd. The platform keeps them next to the project element and reads them by name; - the directories
.git,.claude,.github,__pycache__,node_modules,.venvand all hidden ones (starting with a dot) are excluded; - the files
.gitignore,.env,.DS_Storeand*.xasm,*.xlibfiles are excluded.
Parsing a built archive
The reverse of a build: from a .xasm or .xlib file you get the manifest, the project properties from its Проект.yaml inside the archive, and the contents. It is needed to attach a library to a project without unpacking its sources.
The layout inside a project; only the directories tell the truth about the contents:
- a first-level directory is a subsystem.
Подсистема.yamlis optional, and a library subsystem may have none at all, so it cannot be relied upon when looking for subsystems; - a nested directory of a subsystem is a package. A package has no description file, and every directory contributes a name segment;
- the qualified name of a type:
{vendor}::{name}::{subsystem}[::{package}]::{TypeName}. The same name without the last segment is whatИспользованиеandимпортtake.
Only types with ОбластьВидимости: Глобально are visible outside, in the project that attached the library. The default is ВПодсистеме, and the global scope is written explicitly.
Compatibility is checked against the РежимСовместимости property of Проект.yaml. The ВерсияТехнологии property does not exist in Проект.yaml: it belongs to the body of the Console API request that creates an application, not to the project file.
Platform behaviour you have to account for
- Silent rollback of build apply. When applying a build to the application fails, a compilation error for instance, the platform silently rolls the application back to the previous build and starts it. The
Runningstatus does not mean success. A reliable check of the result looks like this:- take the application tasks (section 4.6) with status
ErrororFailedwhosestart-dateis not earlier than the moment the deploy started. Old errors from history do not count; - compare the actually applied version, the
source.project-versionof the application card, with the version of the uploaded build; - compare a build of an extension with the extensions of the application instead,
extension-projectsofGET /v2.1/applications/{id}/project: the card names the build of the application alone. The extension has to run the version of the build and beenabled. A server without Console API 2.1 cannot tell, so there the apply of an extension stays unverified rather than reported as a rollback; - for information, make a check GET against the application
uri. Codes 401 and 403 are normal for closed applications and do not contradict success.
- take the application tasks (section 4.6) with status
- Empty skeleton on creation. On some platform configurations an application created with a “project” source, meaning
image-idset to the project id, comes out empty, with no project data. A reliable source is a specific build inproject-version-id, for example the project’s latest build. - Deletion with drafts. If the application’s development environment has unpublished edits,
DELETE /applications/{id}returns 400 withFAILED_PRECONDITIONin the body. There is no forced deletion in the API, only the control panel, and the tool must provide a clear hint. - Readiness of a new application. After creation, the application sits in transitional statuses and without a
urifor some time, so provide for waiting until it is ready: aurihas appeared and the status is stable. AnErrorstatus while waiting is an immediate error. - Restart after apply.
project/updatemay restart the application itself. After the call, wait until it leaves the transitional statuses. If the result is notRunning, stop it unless it is alreadyStopped, wait forStopped, start it and wait forRunning. Reasonable waits: about 3 minutes for a stop, about 5 minutes for a start and stabilization, polling every 10 seconds or so. Erroris a final status. A stableError, after a failed apply for instance, is an immediate failure: surface the error messages of the application tasks right away. Do not stop or restart such an application, and do not keep waiting for another status: fromErrorit never moves toStopped, and the wait just burns the whole time budget.- Windows. Temporary files and caches go through
tempfileonly. Switch console output to UTF-8 withreconfigurefor stdout and stderr, otherwise Cyrillic breaks. - The project is identified by its
Ид. See the build upload above: a project is identified by theИдofПроект.yaml, and theVendor+Namepair of the manifest only has to be free of the other projects of the space. An upload without a project id is not “create a new project”, it is “put it where thisИдbelongs”. - Deletion runs in the background and in order.
DELETE /applications/{id}returns immediately, and the application lives on for a while with aDeleteApplicationtask. While it exists, deleting the build it was created from is rejected with a 500. The order for cleanup is: delete the application, wait until its card answers 404 or the status becomesDeleted, and only then delete the build. - Compilation is the server’s, and it happens on apply. A local build only packs an archive. The syntax, the types and the visibility of the sources are checked by the server compiler when a build is applied or an application is created out of it. There is no separate “compile” endpoint. That, together with points 8 and 9, is what
elemctl probeis built out of: the sources go to their own project as a build with a one-off version, the compiler is reached through a throwaway application, and both are removed afterwards. The working application is never at risk. - Signing in to a freshly created application. A new application gets its own empty user list, and the card points at it with
default-user-list. Password sign-in is off, and it has no account service. So the accounts used to sign in to other applications do not work here. Connecting another application’s user list (POST /applications/{id}/userlists) together with enabling the local sign-in does not change that. What does work is a control-panel account: the platform connects its users to the application itself, and they sign in right away. Worth knowing before raising a stand for a task: the way in does not follow from the card, and trying accounts is a bad idea, because a user has a failed-attempt counter. - A server that is still starting. After a start or an update the server takes minutes to bring its console up: thirteen of them were seen after one update. All that time every console request, the token request included, is answered with a 404 whose text names the console application,
Application "console" not found. That is the status a missing application or build gets, so only the text tells them apart.elemctlrecognizes the answer in its client and says what it means: the server is starting, repeat once/consoleanswers 302.deploywaits it out by itself, for up to--server-start-timeoutseconds (900 by default, 0 turns the wait off), and goes on once the console answers. A request the console refused was never processed, so asking again is safe. - A removal is applied without a question. A build whose sources no longer have a tabular part is applied as it is: the part goes, and its rows go with it. No refusal and no warning, the task ends well and the application keeps running; that was seen on a catalog with a row in the part. An attribute of a tabular part and an attribute of the object go the same way, with their values, and so does a catalog whose description is gone from the sources, with its whole table: that was seen on an empty catalog. A description made anew in its own file, under the same path and name but with a new
Ид, is the same loss: a catalog with two rows came out of such an apply empty, and the task ended well. A narrowed length or a changed type recreates the data of the object, soelemctl deployrefuses those unless told otherwise, and it refuses a catalog, a document or a register removed whole the same way. A removed attribute or tabular part it names before the build and goes on: it is a deliberate edit of the sources, and the line says what the apply takes away.