Всичко, което потребител на Lolly натрупва, живее на неговото устройство - без акаунт, без облак. Пакетът за прехвърляне на данни е начинът, по който тази стойност се движи: изнеси я на една инсталация, пренеси файла с каквото и да е средство (USB, AirDrop, имейл до себе си, споделена мрежова папка) и внеси я на друга. Файлът е транспортът. Целта може да е офлайн или онлайн. Няма значение, защото нищо никога не комуникира със сървър.
signed by Lollyvector SVG9 paths~4.9k nodes11 groups56 KBПровери самВземи подписания файл
signed by Lollyvector SVG9 paths~4.9k nodes11 groups56 KBПровери самВземи подписания файл
Тази страница е спецификацията на формата. За описание за крайния потребител виж Намери и възстанови работата си → Премести работата си на друго устройство. Реализацията е в shells/web/src/data-transfer.ts, а tests/data-transfer.test.ts фиксира договора за пренасяне на данните напред-назад.
Обхват. Пакетът носи потребителски данни, не инструменти на каталога. Инструментите и активите от каталога се синхронизират отделно и се приема, че вече присъстват на целта (в най-лошия случай в по-нова версия); инструментите, които потребителят е направил сам, пътуват вътре в
profile.json. Внасянето никога не инсталира или обновява инструмент на каталога.
Цели
- Един формат, всяка обвивка. Уеб PWA, приложенията на Tauri за desktop/mobile и бъдещите обвивки споделят една и съща обвивка на пакета и поддържаните схеми на частите. Незадължителните части зависят от възможностите на всяка обвивка; неподдържаните части се отчитат. Всеки capability bridge осигурява своя адаптер за съхранение.
- Оцелява пътуването. Пакет, повреден или орязан по време на пренос, се проваля шумно при внасяне, никога не възстановява наполовина.
- Надживява тази версия. По-старо приложение все още може да внесе разпознатите части на по-нов пакет. Действително прекъсваща промяна на формата се отказва чисто.
- Безопасно за сливане. Внасянето върху вече използвана инсталация никога не изтрива нещо, което не е било в пакета.
Обвивката
Пакетът е обикновен .zip. Изтегленият файл е именуван на човека, на когото принадлежи - LollyTools-<First>-<Last>-<YYYY-MM-DD>-<n>.zip (например LollyTools-Ada-Lovelace-2026-06-26-1.zip) - така че папката с изтегляния, съдържаща резервни копия, остава четлива. Частите за собствено и фамилно име идват от профила и се пропускат, ако не са зададени. Липсващ профил дава LollyTools-2026-06-26-1.zip, а само собствено име дава LollyTools-Ada-2026-06-26-1.zip. Всяка част се пречиства до безопасен за файлово име токен (запазват се Unicode букви/цифри, премахват се интервали/пунктуация, ограничение от 32 знака). <n> е последователност за ден и устройство, така че повторни експорти в един и същи ден не се сблъскват и остават подредени. backupFilename() в shells/web/src/data-transfer.ts изгражда името. Съдържанието на zip файла е еднакво независимо от името. Вътре:
| Път | Задължителен | Съдържание |
|---|---|---|
manifest.json | да | Id на формата, версии, брой елементи и цялостност по части. Първото нещо, което чете четецът. |
profile.json | ако е зададен | Целият запис me на потребителя: име, контакт, връзка към снимка и флагове, плюс папки, кошче, проектни бланки, потребителски шаблони и инструменти, направени от потребителя, любими, скрити инструменти, език и избор на емоджи. Чете се чрез host.profile. |
sessions.json | да | Всяка запазена сесия: слот, id/версия на инструмента, етикет, миниатюра (data-URL) и пълни входни данни. Чете се чрез host.state. |
assets.json | да | Метаданни за всеки качен актив (изображения, шрифтове, брандови токени, лога, запазени копия на изтегляния), всяко сочещо към своите байтове под assets/blobs/. |
assets/blobs/<n>.<ext> | по актив | Суровите байтове на актива (файлове с изображения и шрифтове). Съхраняват се некомпресирани (вече компресирани формати). Разширението е козметично. MIME типът в assets.json е меродавен. |
assets/blobs/<n>.c2pa | когато е налично | Извлечени Content Credentials като точни двоични байтове, посочени от _credentialFile в записа на актива. Те не са ключове за подписване на устройството. |
design-systems.json | когато е налично | Дизайн системите, направени или добавени на тази инсталация, като { active, records }. Сливат се по id при внасяне; активният избор на пакета важи само когато целта няма собствена дизайн система. |
file-history.json | по избор | Версионирани снимки на активи, крайни отчети за файлови операции и пълни манифести на batch. Частта за история има собствена версия; осигурена е от вътрешния адаптер за резервно копиране fileHistory на обвивката. |
revision-history.json | по избор, ръчни резервни копия | Стабилни ID на творения, запазени контролни точки, миниатюри и текущи чернови за възстановяване. Осигурена чрез host.state.history.backup, където се поддържа. |
file-history/versions/ | за всяка снимка | Предишни байтове на актив и извлечени credentials, независимо дали текущият актив все още съществува. |
file-history/results/ | за всяка завършена операция | Точни байтове на изхода. Никой оригинален файл, избран за конвертиране, не се запазва или включва. |
prefs.json | да | Локални предпочитания, притежавани от потребителя: theme, sidebarWidth и брояча на активност ct-metrics. |
lolly.txt | да | Четимо от човек резюме на пакета (брой елементи, профил, име на файл) за всеки, който отваря zip-а без Lolly. Прегенерира се при всеки експорт и се разпознава при внасяне, така че никога не се брои за пропусната част. Записва се след картата на цялостността, така че остава извън нея. |
Пакетът е обикновен zip нарочно: оцелява всеки транспорт непокътнат, а всеки инструмент за разархивиране може да го инспектира.
profile.json е най-малката част и тази, която потребителят вижда пръв в приложението: данните, попълнени веднъж от него, плюс опцията, която позволява на инструментите да ги ползват.
signed by Lollyvector SVG15 paths~1.2k nodes30 groups1 image40 KBПровери самВземи подписания файл
manifest.json
{
"format": "lolly-backup",
"formatVersion": 3,
"minReader": 1,
"app": "lolly",
"exportedAt": "2026-06-22T09:30:00.000Z",
"counts": { "profile": true, "sessions": 2, "userAssets": 4, "prefs": 3, "assetVersions": 1, "fileOperations": 1 },
"integrity": {
"profile.json": "sha256-…",
"sessions.json": "sha256-…",
"assets.json": "sha256-…",
"assets/blobs/0.bin": "sha256-…",
"file-history.json": "sha256-…",
"file-history/versions/0.bin": "sha256-…",
"file-history/results/0.bin": "sha256-…",
"prefs.json": "sha256-…"
}
}| Поле | Значение |
|---|---|
format | Винаги lolly-backup. Файл без него се отхвърля като „не е резервно копие на Lolly“. |
formatVersion | Оформлението, с което е записан този пакет. Увеличава се при всяка промяна на набора или формата на частите. Четците не се базират на него. |
minReader | Минималната версия на четеца, необходима за безопасно внасяне на този пакет. Това е полето, на което се базират четците. |
app | Id на произвеждащото приложение, за диагностика. |
exportedAt | ISO времеви печат на създаването на пакета. |
counts | Какво е сложил писателят, за показване и проверка за здравина. |
integrity | По избор. Свързва всяка част освен manifest.json с дайджест във формат SRI - sha256-<base64> - на нейните некомпресирани байтове. |
Политика за версии (обратна съвместимост напред)
Разделянето между formatVersion и minReader е това, което позволява на формата да расте, без да изолира по-стари инсталации:
- Четец внася пакет, когато
manifest.minReader ≤собствената му версия на четеца. Отказва (с „нуждае се от по-нова версия на приложението“) само когато пакетът изрично изисква по-нов четец. - Добавяща промяна - нова по избор част или ново по избор поле в манифеста - увеличава
formatVersion, но оставяminReaderнепроменен. По-старите приложения продължават да внасят всяка част, която разпознават. Части, които не разпознават, се пропускат (виж по-долу), а не се изхвърлят мълчаливо. - Прекъсваща промяна - такава, при която грешно внасяне на част поврежда данни, или при която досега по избор част става задължителна - вдига
minReader. По-старите приложения тогава отказват чисто, вместо да внасят нещо, което не могат да обработят. - Ако бъдещ пакет зададе
formatVersion, но пропуснеminReader, четците консервативно се връщат към базиране наformatVersion(третират промяната като прекъсваща).
Правило на палеца за автори: ако всеки съществуващ четец все пак ще постъпи правилно, като игнорира добавката ти, тя е добавяща - увеличи
formatVersion, оставиminReader. В противен случай вдигниminReader.
Цялостност
Когато manifest.integrity присъства, четец проверява SHA-256 на всяка изброена част преди да запише каквото и да е. Несъответствие („не премина проверката за цялостност“) или липсваща част („непълен“) прекратява цялото внасяне - няма частично възстановяване. Това улавя повредата, която транспортът на файл може да внесе (орязан AirDrop, имейл шлюз, който е прекодирал прикачения файл, лош USB сектор).
Цялостността е по замисъл best-effort: записва се само там, където Web Crypto е налична (всеки сигурен контекст на браузър и съвременен Node), и се проверява само когато и картата, и Web Crypto присъстват. Пакет без картата - например такъв отпреди появата на цялостността - се внася непроменен. „Не може да се провери“ никога не се третира като „повреден“.
Манифестът не изброява нито себе си, нито прегенерирания README lolly.txt. Дайджестите покриват частите, за които манифестът гарантира.
Семантика на внасянето
Внасянето е сливане, никога пълна замяна:
- Съществуващите данни на целта остават на място.
- Когато слот на сесия или ID на качено изображение присъства и на двете места, се запазва копието, записано по-скоро, така че по-старо резервно копие никога не презаписва по-нова работа на целта. Равни или неизвестни времена запазват копието на целта. При уеб инсталация с история на творенията същото правило решава кое копие на творение остава текущо, а другото копие се пази като защитена чернова (виж по-долу).
- Профилният запис се слива, а не се заменя. Всяка папка на целта остава със съдържанието си; папка от пакета, която липсва на целта, се добавя, а папка, налична и на двете места, запазва името и родителя на целта и получава липсващите ѝ членове от пакета. Сесия, подредена в папка на целта, остава подредена там.
- Любимите (инструменти, активи от каталога и елементи от Проекти) се обединяват. Шаблони, шаблони на Проекти и потребителски инструменти от пакета се добавят, когато целта няма запис с този ID. Записите в Кошчето от двете страни се запазват, така че елемент, който е можел да бъде възстановен на която и да е инсталация, все още може.
- Всяко друго поле на профила (име, данни за контакт, език, функционални флагове, скрити инструменти и другите настройки) запазва стойността на целта. Поле, което е празно на целта, приема стойността от пакета. Същото важи за
prefs.json: предпочитание се записва само там, където целта няма такова. - Обикновеното прилагане на синхронизацията между устройства е изключението: пазейки устройствата в крак, то взема профилния запис, предпочитанията, сесиите и изображенията от синхронизираното копие. Възстановяването на по-ранно копие прави същото, тъй като нарочно се връща назад във времето. Първото присъединяване, Пренеси го на това устройство, слива както при внасяне.
- Историческите версии на активи и ID на операции са неизменни изключения: повторно внасяне е идемпотентно, а ID, който вече назовава различни байтове/история, се отказва, а не се презаписва. Повторното внасяне на непроменен текущ актив запазва версията му. Променен текущ актив трябва да носи различна версия.
- Историята на творенията също се слива. Творение, налично от двете страни, запазва като текущо копието, записано по-скоро, а другото копие - като защитена чернова; творение, чийто слот целта използва за друго творение, се добавя до него; творение в Кошчето на целта остава там. ID на контролна точка, който на целта назовава различно съдържание, запазва това на целта. Архив, който не преминава собствените си проверки, спира внасянето преди каквато и да е промяна в профила, сесията, актива или предпочитанията. Идентично повторно внасяне не добавя съхранение.
- Нищо, което не е било в пакета, не се докосва. Сесия, която целта е имала, но пакета - не, оцелява при внасянето.
Запазените сесии се повторно свързват към своите изображения автоматично: препратките към активи се пазят по id, а мостът ги преразрешава наново, след като качените изображения бъдат възстановени (задължително, тъй като URL адресите blob: не оцеляват при презареждане).
Резюмето на внасянето отчита { profile, sessions, userAssets, prefs, skipped, failedAssets }. failedAssets брои качени активи, които не са могли да бъдат възстановени (например пълно устройство за съхранение). Различно е от skipped, което брои части от по-нов писател с обратна съвместимост, които тази версия не е разпознала. Интерфейсът показва skipped („… · N по-нови елемента пропуснати“), така че възстановяването е честно относно това, което е оставило зад себе си.
Когато е налична историята на файлове, резюмето носи и assetVersions, fileOperations и failedHistory. Изчерпване на хранилището или конфликти на неизменни ID могат да причинят частично възстановяване; интерфейсът казва на потребителя да запази изходното резервно копие. Облачната синхронизация не напредва приложената си ревизия след частично или неподдържано възстановяване, така че снимката остава налична за повторен опит. Възстановяването не е една единствена транзакция за всички хранилища на профил/сесия/актив/история.
История на творенията (v3)
Ръчните резервни копия от уеб хост, поддържащ история, включват revision-history.json със собствена схема { version: 1, documents, revisions, recoveries }. Тя носи запазените ID, каноничните снимки на входните данни, версийните печати, растерните превюта и отделните чернови на автора. Адаптерът за история улавя текущите сесии и техните върхове в една транзакция за четене; sessions.json използва същите тези текущи снимки за по-стари четци.
Възстановяването проверява SHA-256 и броя байтове на полезния товар, уникалните идентичности, връзките документ/връх, произхода, времевите печати, типовете превюта и лимитите, преди да приложи архива в една транзакция. Компресирани препратки към родители може да липсват. Съществуващата текуща работа никога не се заменя мълчаливо: когато творение е налично от двете страни, страната, която не е запазена като текуща, става защитена чернова. Лимитът от 384 MiB за пренос на архива се проверява изрично, а лимитите за съхранение се налагат, без да съкращават запазените контролни точки. Цялостното резервно копие все още използва in-memory ZIP реализация и не е стрийминг архив.
Резюмето добавя revisions и recoveryDrafts, като брои само това, което е добавил този импорт, и added, kept, replaced, copies и hidden - как е слято всяко творение. Обвивка без тази възможност възстановява обикновени сесии и отчита частта за история като пропусната. Историята на native файловата система остава неподдържана, докато нейният адаптер не осигури трайни транзакции за история. Гост състоянието на P2P няма трайна история или архив за възстановяване.
Личната синхронизация на снимки изрично изключва историята на творенията. Прилагането на снимка към локален документ с история запазва предишното му работно състояние като отделна чернова за възстановяване и анулира write токена на всеки отворен редактор. Неговите неизменни контролни точки остават на устройството. Това защитава локалната история по време на замяна със снимка; не слива историите на едновременни устройства.
Историческите препратки към активи се запазват, докато рендирането все още разрешава активите през съществуващата библиотека на местоназначението. Този архив все още не гарантира точните стари байтове на актива или старите рендери на инструмента. Байтовете на версии на активи и резултати от файлове продължават да пътуват през своята съществуваща отделна част от резервното копие.
Запазени версии и резултати от файлове (v2)
Незадължителната част за история съдържа { version: 2, assetVersions: [...], operations: [...], batches: [...] }; четците приемат и по-ранната форма history-v1 без batches. Всяка снимка идентифицира стабилния ID на актива и точната версия, времето на запазване, дължината в байтове и hex SHA-256, плюс запис на актив, чиито _file и незадължителен _credentialFile сочат към бинарни части. Операциите носят оригиналните факти за файла, заявката, отчета, времевите печати и незадължителен резултат _file; имената на бекенда за съхранение, дескрипторите на OPFS и лизингите за изпълнение не пътуват. По-стари четци, поддържащи само history-v1, отказват новата версия на историята преди внасяне, вместо мълчаливо да изпуснат членството в batch.
Манифестите на batch записват всеки избран източник преди обработка, включително файлове, които никога не са прочетени, отменени членове, неуспехи при резервиране на място за резултат и прекъсната работа. Всеки член има стабилен ID на операция, препратка/факти за източника, заявено име на изхода и краен отчет. Непрочетен източник има декларирани факти, а не измислен дайджест. Внасянето валидира идентичността на члена и съгласуваността с всеки пренесен отчет за операция. Отчетите за batch остават налични, когато отделни резултати са изрично премахнати, но разписка не означава, че байтовете на нейния изход все още се съхраняват.
- Всеки известен запис на история, отчет и посочен файл се валидира преди всякакви записи при внасяне на профил или актив. Липсващи байтове и несъответстващ SHA-256 провалят внасянето, дори ако обвивката няма карта на цялостността. Извлечените credentials остават байтови масиви, включително при внасяне от по-стари писатели, които са ги JSON-сериализирали като обекти с числови ключове.
- Работещи операции стават прекъснати записи в резервното копие, с обясняващ отчет за неуспех и без резултат. Възстановяването никога не рестартира фонова работа и не внася активен лизинг. Повторният опит изисква избор на оригиналния файл, проверен спрямо записания му SHA-256, когато е наличен.
- Възстановените резултати записват байтовете и метаданните си заедно в IndexedDB. Обикновените нови резултати използват OPFS, където е налично, с резервен вариант IndexedDB. Съществуваща активна операция никога не се заменя от внасяне.
- Сглобяването на ZIP историята все още е в паметта: текущият лимит е 256 MiB полезен товар на историята, 4 MiB метаданни на историята, най-много 100 операции, 100 batches и 2000 снимки. Експортът изрично отказва прекалено голяма или непълна история; никога не я пропуска мълчаливо. Изтегляй важни версии/резултати поотделно, преди да премахнеш по-стари локални копия. Тези лимити не са измерена гаранция за пиковата памет на телефони.
- Локалната история на резултати има бюджет от 512 MiB и таван от 100 записа. Снимките на активи имат отделен бюджет от 512 MiB и най-много 20 исторически версии на актив; извлечените байтове на credential се броят към този бюджет за снимки. Възстановяването зачита тези лимити и никога не изтрива мълчаливо съществуващи потребителски данни.
- Локалните метаданни на batch имат отделен бюджет от 4 MiB, най-много 100 манифеста и 20 члена на batch. Чакащите членове резервират капацитет за метаданни, с таван от 32 KiB отчет на член. Това е логически бюджет, а не гаранция за дисково пространство на браузъра; реален отказ по квота се показва, а отчетът в паметта остава наличен за изтегляне. Повторен опит с член на batch създава нов batch, без да презаписва стария отчет. Премахването на запис за batch не премахва байтовете на отделни резултати или активите в библиотеката.
- Конвертирани резултати могат изрично да се добавят в библиотеката без нормализация или прекодиране. Хешовете на източника/изхода и връзката с операцията придружават актива. Повторните добавяния преизползват непроменено копие; редактирано копие никога не се презаписва. Растерни изображения могат да започнат нов документ Design. Този документ използва текущия ID на актив от библиотеката: налагането на точни версийни пинове през целия runtime и URL път на Design остава отделна работа. Резултатите SVG/HTML/PDF/ZIP се пазят от това предаване като непрозрачни файлови активи, не се повишават до доверено интерактивно или векторно съдържание.
- Convert → Recent file operations показва използването на историята, отчетите, изтеглянията и мениджъра на версии. Мениджърът намира и по-стари версии на изтрити активи от библиотеката. Възстановяването на снимка създава нова текуща версия, докато запазва избраната снимка непокътната. Настройки → Хранилище отчита резултатите и версиите отделно от временните кешове.
- Изричното почистване на временни файлове премахва само байтове, притежавани от операция и без препратки към тях. Текущите записи защитават файловете си; скорошните OPFS файлове имат едночасов гратисен период. Запазените резултати и снимки на активи не се изчистват автоматично.
По-старите четци все още приемат обвивката v2 (minReader: 1) и възстановяват познатите части, като броят неподдържаните части на историята като пропуснати. Пълното възстановяване на историята изисква обвивка с адаптера fileHistory; това е вътрешен за обвивката шев, не нова възможност на HostV1, обърната към инструментите. Истинското възстановяване между две устройства е покрито от локалния Chromium gate; приемането на възстановяване в инсталирани Tauri/iOS/Android остава отделно.
Какво не пътува
- Кешове на каталога (изтеглени метаданни и blobs на активи, индексът на инструментите) - синхронизират се безплатно наново на целта.
- Инструменти и активи на бранда - извън обхвата, и се приема, че вече присъстват на целта. Брандовите токени, шрифтове и лога, добавени от потребителя, са потребителски активи, така че те пътуват.
- URL адреси
blob:/ object URL - прегенерирани от моста при зареждане. - Оригинали за конвертиране, активни лизинги за изпълнение и локални за машината тайни за достъп/подписване - не са преносим товар за история. Запазен резултат е копие, а не обещание, че оригиналният източник е бил архивиран.
- Броячът на последователността за експорт - брояч за именуване на изтеглянията по ден (ключ на
localStoragelolly-export-seq) е локално удобство за именуване. Държи се извънPREF_KEYS, така че никога не пътува в пакет.
Индикаторът за съхранение изброява същото разделение. Запазените сесии, My images и File results & versions пътуват в пакет. Кешът на активите, преглед на инструментите и офлайн закачените под тях всички са възстановими наново, така че остават извън него.
signed by Lollyvector SVG37 paths~4.9k nodes85 groups65 KBПровери самВземи подписания файл
signed by Lollyvector SVG37 paths~4.9k nodes85 groups65 KBПровери самВземи подписания файл
Гаранция между обвивки
data-transfer.ts чете и записва изключително през capability bridge (host.profile, host.state, host.assets) и споделените localStorage предпочитания. Същият модул чете и записва общата обвивка в уеб и Tauri, през IndexedDB или файлова система. Незадължителните части на историята се появяват само там, където съответният адаптер е наличен; неподдържана част се отчита като пропусната при внасяне. Headless комплектът тества общите части срещу in-memory bridge, докато транзакциите на историята имат и тестове в реален браузър.
Две обвивки остават извън тази гаранция, по различни причини:
- Еднократният CLI няма какво да пренася - състоянието му е in-memory и ефимерно за всяко извикване.
- TUI наистина запазва състояние (
~/.lolly: сесии, папки, профил) и неговият Profile изглед може да го архивира, но записва по-прост собствен архив:saved-state/<slot>.jsonза всяка сесия плюсprofile.jsonиfolders.json, без манифест, безformatVersion/minReaderи без карта за цялост. Той не е импортируем в този формат - четецът го отхвърля като „not a Lolly backup“ - и объркващо използва подобно име (lolly-backup-<stamp>.zip). Обединяването на двата е известен пропуск.
Запазени точки за разширение
Обвивката е манифест плюс набор от именувани части по замисъл, така че по-нови видове преносими данни да могат да я използват по-късно без пробиваща промяна. Те се вграждат като допълнителни части (нов formatVersion, същия minReader), а днешният четец пропуска това, което не разпознава. Това още не е реализирано. Имената са запазени тук, за да остане форматът съгласуван, когато бъдат добавени.
tokens.json- дизайн токъни. Документ с дизайн токъни по W3C DTCG (форматът, който Penpot импортира и експортира - токъни с$value/$type/$description, организирани в групи, набори и теми). Набор от токъни в пакета позволява на потребителя да пренася своите брандови примитиви между инсталации заедно със сесиите си. (Собствените брандови токъни на потребителя вече пътуват днес като активаuser/tokens/brandвassets.json; тази част би носила цял DTCG документ с неговите набори и теми.) В по-дългосрочен план внесен набор от токъни ще стане пълноправен източник, спрямо който инструментите и палитрите се разрешават.penpot/- внесени Penpot файлове. Запазена директория за Penpot файл (или извлечено от него подмножество, релевантно за Lolly), внесен и показан като инструмент. Пакетът ще носи внесената дефиниция, така че тя пътува заедно с останалите данни на потребителя.
Всичко извън тези запазени имена и частите по-горе е за четеца непозната част: оставя се недокоснато и се брои в skipped.
Справочник
- Модул:
shells/web/src/data-transfer.ts(exportBackup,importBackup,BACKUP_FORMAT,BACKUP_FORMAT_VERSION,BACKUP_READER_VERSION- именованетоbackupFilename()е вътрешно). - Контрактен тест:
tests/data-transfer.test.ts- случаи на round-trip, сливане, цялост, съвместимост напред и заграждане на четеца. - Контрактни тестове за история:
tests/file-history-backup.test.ts,tests/file-batch-history.test.tsиtests/file-result-library.test.ts. Browser приемане:tests/file-history.browser.test.ts,tests/file-batch-history.browser.test.tsиtests/file-result-reuse.browser.test.ts. - Използвана повърхност на bridge:
host.profile,host.state,host.assets- вж. Host API.