Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0997c03455 | ||
|
|
c3dc185f60 | ||
|
|
a27f670233 | ||
|
|
1d237ca5a7 | ||
|
|
391e96df57 | ||
|
|
d34f4b0eb2 | ||
|
|
bf4df69898 | ||
|
|
57a7a13068 | ||
|
|
202a670357 | ||
|
|
e90d29b463 | ||
|
|
2b134f32ef | ||
|
|
a4f07b96db | ||
|
|
3763eb796b | ||
|
|
2fa190b0a5 | ||
|
|
269dda70f4 | ||
|
|
0c78ec3d44 | ||
|
|
0638cda731 | ||
|
|
ecbecf8fb0 | ||
|
|
e5199bc6c4 | ||
|
|
bb0e28b577 | ||
|
|
7130a731d1 | ||
|
|
7bb8df8cdf | ||
|
|
6147e04335 | ||
|
|
23c45f32f4 | ||
|
|
e39dc16db5 | ||
|
|
0051587103 | ||
|
|
8c33f5348b | ||
|
|
0c7fc80337 | ||
|
|
3341063f8f | ||
|
|
41ad93dac6 | ||
|
|
f994936f91 | ||
|
|
a10f61d9d9 | ||
|
|
d1863cb19d | ||
|
|
b3e5e7fcc6 | ||
|
|
9399e09a1a | ||
|
|
12e6613f08 | ||
|
|
84d9fefe94 | ||
|
|
284d7f2295 | ||
|
|
b625fe7792 | ||
|
|
c962efb884 | ||
|
|
752f58a27b | ||
|
|
c846c3c1ad | ||
|
|
9659e8d2ad | ||
|
|
329dfc91bf | ||
|
|
45a1a52228 | ||
|
|
6a081e152e | ||
|
|
9e82384a23 | ||
|
|
8bb586f57e | ||
|
|
5593e8e0ce | ||
|
|
e52181447b | ||
|
|
88ecc1ffd5 | ||
|
|
f100199f31 | ||
|
|
6005ff3dfe | ||
|
|
77ff83c44b | ||
|
|
9a6f8ab775 | ||
|
|
ec44e81b8b | ||
|
|
d85b904c46 | ||
|
|
10a68ad561 | ||
|
|
adcce61d77 | ||
|
|
7f0a16e58e | ||
|
|
c4bc75572a | ||
|
|
fd8bbd365b | ||
|
|
fbee75d76b | ||
|
|
a9ba9a1023 | ||
|
|
519002902c | ||
|
|
e86208ddfe | ||
|
|
26e36de417 | ||
|
|
103f8c456a | ||
|
|
9f900ca57f |
No files matched your search
@@ -9,6 +9,7 @@ A community app store for [Umbrel](https://umbrel.com), store id `whatsnext`. Ad
|
||||
| **Evolu Relay** | Run the sync relay behind Trezor Suite's labels on your own Umbrel, with an owner allowlist. |
|
||||
| **Atreides Assets** | A self-hosted overview of your crypto and precious metals. |
|
||||
| **Atreides Services** | Your own price feed for Atreides Assets, recorded around the clock. Early version. |
|
||||
| **Schoolbord** | Tenants, agreements and monthly invoices for a small venue, in Dutch. Early version. |
|
||||
|
||||
The image recipes for the first two are in [tools/](tools/); the tests are in [tests/](tests/).
|
||||
|
||||
|
||||
+204
-1
@@ -44,8 +44,12 @@ STORE = os.path.join(REPO, "umbrel-app-store.yml")
|
||||
|
||||
# De volgorde uit de packaging-documentatie. Wat de spec niet noemt (icon,
|
||||
# backupIgnore, submitter, submission) mag erachter, niet ertussen.
|
||||
#
|
||||
# `storage`, `folderAccess` en `environment` zijn er sinds umbrelOS 2.0 bij en
|
||||
# staan direct na `id` (skill umbrel-package-app, opgehaald 09-10-2026).
|
||||
VOORGESCHREVEN = [
|
||||
"manifestVersion", "id", "category", "name", "version", "tagline",
|
||||
"manifestVersion", "id", "storage", "folderAccess", "environment",
|
||||
"category", "name", "version", "tagline",
|
||||
"description", "releaseNotes", "developer", "website", "dependencies",
|
||||
"repo", "support", "port", "gallery", "path",
|
||||
]
|
||||
@@ -327,6 +331,203 @@ def test_data_onder_data(u, app):
|
||||
not ontbreekt, "ontbreekt: %r" % ontbreekt)
|
||||
|
||||
|
||||
# De mount-opties die umbreld toestaat op een bron onder ${APP_DATA_DIR}/data
|
||||
# zodra de datamap verplaatsbaar is. Elke andere optie laat de compose falen met
|
||||
# [apps-data-root-invalid-compose]. Uit app.ts van umbreld, 09-10-2026.
|
||||
DATAROOT_OPTIES = {
|
||||
"ro", "rw", "private", "rprivate", "shared", "rshared", "slave", "rslave",
|
||||
"z", "Z", "consistent", "cached", "delegated",
|
||||
}
|
||||
|
||||
|
||||
def test_verplaatsbare_opslag(u, app):
|
||||
"""Een app met `storage:` heeft precies `dataRoot: data`, en mounts die umbreld kan omleiden.
|
||||
|
||||
Dit is het enige manifestveld dat umbreld wél streng valideert: elke andere
|
||||
waarde dan `data` maakt het manifest ongeldig, en dan verschijnt de app niet.
|
||||
Met het veld erin herschrijft umbreld elke bron onder ${APP_DATA_DIR}/data naar
|
||||
de plek die de gebruiker kiest, en weigert dan een pad dat uit data/ ontsnapt
|
||||
of een mount-optie die niet in DATAROOT_OPTIES staat.
|
||||
|
||||
Een app zonder `storage:` valt hier buiten; het veld is optioneel. Maar het
|
||||
kan er niet meer uit als het er eenmaal in staat: een installatie die al
|
||||
verplaatst is, start dan niet meer.
|
||||
"""
|
||||
tekst = lees(os.path.join(REPO, app, "umbrel-app.yml"))
|
||||
regels = tekst.splitlines()
|
||||
if not any(r.startswith("storage:") for r in regels):
|
||||
return
|
||||
|
||||
i = next(i for i, r in enumerate(regels) if r.startswith("storage:"))
|
||||
blok = []
|
||||
for r in regels[i + 1:]:
|
||||
if r and not r[0].isspace():
|
||||
break
|
||||
if r.strip() and not r.strip().startswith("#"):
|
||||
blok.append(r.strip())
|
||||
u.check("%s: storage heeft precies dataRoot: data" % app,
|
||||
blok == ["dataRoot: data"], "gevonden: %r" % blok)
|
||||
|
||||
pad = os.path.join(REPO, app, "docker-compose.yml")
|
||||
if not os.path.isfile(pad):
|
||||
return
|
||||
fout = []
|
||||
for regel in lees(pad).splitlines():
|
||||
kaal = regel.strip()
|
||||
if not kaal.startswith("- ${APP_DATA_DIR}/data"):
|
||||
continue
|
||||
delen = kaal[2:].strip().strip('"').split(":")
|
||||
bron = delen[0][len("${APP_DATA_DIR}/"):]
|
||||
if ".." in bron.split("/"):
|
||||
fout.append(kaal)
|
||||
if len(delen) > 2:
|
||||
opties = delen[2].split(",")
|
||||
if any(o not in DATAROOT_OPTIES for o in opties):
|
||||
fout.append(kaal)
|
||||
u.check("%s: elke mount onder data/ kan umbreld omleiden" % app,
|
||||
not fout, "geweigerd: %r" % fout)
|
||||
|
||||
|
||||
def folder_slots(tekst):
|
||||
"""De slots onder `folderAccess:` als lijst van dicts, elk met een lijst `mounts`.
|
||||
|
||||
Geen YAML-lezer, zie veld(). Wat telt is de inspringing: een streepje op de
|
||||
diepte van het eerste streepje begint een slot, een dieper streepje een mount
|
||||
in dat slot. Een sleutel zonder streepje hoort bij de mount zolang hij dieper
|
||||
staat dan de sleutels van het slot.
|
||||
"""
|
||||
regels = tekst.splitlines()
|
||||
start = next((i for i, r in enumerate(regels) if r.startswith("folderAccess:")), None)
|
||||
if start is None:
|
||||
return None
|
||||
|
||||
slots = []
|
||||
slot_streep = None
|
||||
huidig = doel = None
|
||||
for r in regels[start + 1:]:
|
||||
if r and not r[0].isspace():
|
||||
break
|
||||
kaal = r.strip()
|
||||
if not kaal or kaal.startswith("#"):
|
||||
continue
|
||||
inspring = len(r) - len(r.lstrip())
|
||||
if kaal.startswith("- "):
|
||||
kaal = kaal[2:].strip()
|
||||
if slot_streep is None or inspring <= slot_streep:
|
||||
slot_streep = inspring
|
||||
huidig = doel = {"mounts": []}
|
||||
slots.append(huidig)
|
||||
elif huidig is not None:
|
||||
doel = {}
|
||||
huidig["mounts"].append(doel)
|
||||
elif huidig is not None and doel is not huidig and inspring <= slot_streep + 2:
|
||||
doel = huidig
|
||||
if doel is None or ":" not in kaal:
|
||||
continue
|
||||
sleutel, waarde = kaal.split(":", 1)
|
||||
waarde = waarde.strip().strip('"').strip("'")
|
||||
if waarde:
|
||||
doel[sleutel.strip()] = waarde
|
||||
return slots
|
||||
|
||||
|
||||
def doelpaden_per_service(tekst):
|
||||
"""Per service in de compose de doelpaden van de korte `volumes:`-regels."""
|
||||
uit = {}
|
||||
in_services = False
|
||||
service = None
|
||||
in_volumes = False
|
||||
volumes_inspring = 0
|
||||
for regel in tekst.splitlines():
|
||||
if regel.startswith("services:"):
|
||||
in_services = True
|
||||
continue
|
||||
if not in_services:
|
||||
continue
|
||||
if regel and not regel[0].isspace():
|
||||
break
|
||||
kaal = regel.strip()
|
||||
if not kaal or kaal.startswith("#"):
|
||||
continue
|
||||
inspring = len(regel) - len(regel.lstrip())
|
||||
if inspring == 2 and kaal.endswith(":"):
|
||||
service = kaal[:-1]
|
||||
uit[service] = []
|
||||
in_volumes = False
|
||||
continue
|
||||
if service is None:
|
||||
continue
|
||||
if kaal == "volumes:":
|
||||
in_volumes = True
|
||||
volumes_inspring = inspring
|
||||
continue
|
||||
if in_volumes:
|
||||
if kaal.startswith("- "):
|
||||
delen = kaal[2:].strip().strip('"').split(":")
|
||||
if len(delen) >= 2:
|
||||
uit[service].append(delen[1])
|
||||
elif inspring <= volumes_inspring:
|
||||
in_volumes = False
|
||||
return uit
|
||||
|
||||
|
||||
def test_folder_access(u, app):
|
||||
"""Elk `folderAccess`-slot wijst naar een service die bestaat, met een standaardmount.
|
||||
|
||||
umbrelOS 2.0 bewaart de keuze van de gebruiker per `id`, en zoekt de standaard
|
||||
van een slot in de compose: een mount op dezelfde `service` en hetzelfde
|
||||
`targetPath`. Klopt een van de twee niet, dan meldt niemand iets: het slot is
|
||||
dan "Optional" met een lege standaard, en tot de gebruiker een map kiest
|
||||
schrijft de app in wat er toevallig onder dat pad ligt, in het slechtste geval
|
||||
zijn eigen containerlaag, die bij de volgende update weg is. In deze store heeft elk slot
|
||||
daarom een standaardmount onder data/; dat die map in de repo staat, toetst
|
||||
test_data_onder_data.
|
||||
|
||||
`umbrel-downloads` is gereserveerd voor de gedeelde Downloads-map. Zie
|
||||
Umbrel-appstore-spec.md §7.
|
||||
"""
|
||||
slots = folder_slots(lees(os.path.join(REPO, app, "umbrel-app.yml")))
|
||||
if slots is None:
|
||||
return
|
||||
|
||||
u.check("%s: folderAccess heeft minstens een slot" % app, bool(slots))
|
||||
|
||||
ids = [s.get("id") for s in slots]
|
||||
fout_id = [i for i in ids if not i or i != i.lower() or " " in i or "_" in i
|
||||
or i == "umbrel-downloads"]
|
||||
u.check("%s: elk slot-id is lowercase kebab-case en niet gereserveerd" % app,
|
||||
not fout_id, "ids: %r" % ids)
|
||||
u.check("%s: de slot-ids zijn uniek" % app,
|
||||
len(set(ids)) == len(ids), "ids: %r" % ids)
|
||||
zonder_naam = [s.get("id") for s in slots if not s.get("name")]
|
||||
u.check("%s: elk slot heeft een naam" % app,
|
||||
not zonder_naam, "zonder naam: %r" % zonder_naam)
|
||||
|
||||
pad = os.path.join(REPO, app, "docker-compose.yml")
|
||||
if not os.path.isfile(pad):
|
||||
return
|
||||
doelen = doelpaden_per_service(lees(pad))
|
||||
|
||||
zonder_mount = [s.get("id") for s in slots if not s["mounts"]]
|
||||
u.check("%s: elk slot heeft minstens een mount" % app,
|
||||
not zonder_mount, "zonder mount: %r" % zonder_mount)
|
||||
|
||||
onbekend = []
|
||||
zonder_standaard = []
|
||||
for s in slots:
|
||||
for m in s["mounts"]:
|
||||
service = m.get("service")
|
||||
doel = m.get("targetPath", "")
|
||||
if service not in doelen:
|
||||
onbekend.append("%s: %r" % (s.get("id"), service))
|
||||
elif doel not in doelen[service]:
|
||||
zonder_standaard.append("%s: %s op %s" % (s.get("id"), doel, service))
|
||||
u.check("%s: elke slot-mount wijst naar een service die bestaat" % app,
|
||||
not onbekend, "onbekend: %r, aanwezig: %r" % (onbekend, sorted(doelen)))
|
||||
u.check("%s: elke slot-mount heeft een standaardmount in de compose" % app,
|
||||
not zonder_standaard, "ontbreekt: %r" % zonder_standaard)
|
||||
|
||||
|
||||
def test_containernamen_zijn_volledig(u, app):
|
||||
"""Verwijzingen tussen containers gebruiken de volledige naam.
|
||||
|
||||
@@ -534,6 +735,8 @@ def main():
|
||||
test_compose_bestaat_en_hangt_samen(u, app)
|
||||
test_image_tag_is_de_gebouwde(u, app)
|
||||
test_data_onder_data(u, app)
|
||||
test_verplaatsbare_opslag(u, app)
|
||||
test_folder_access(u, app)
|
||||
test_containernamen_zijn_volledig(u, app)
|
||||
test_geen_werkbestanden_in_de_app_map(u, app)
|
||||
test_templates_hebben_geen_losse_dollars(u, app)
|
||||
|
||||
@@ -419,8 +419,12 @@ def test_data_onder_data(u):
|
||||
# De volgorde die de packaging-documentatie van de officiele appstore voorschrijft.
|
||||
# Wat de spec niet noemt, zoals icon en backupIgnore, hoort daarachter en niet
|
||||
# ertussen: dan is de kop van het manifest letterlijk goed.
|
||||
#
|
||||
# `storage` staat sinds umbrelOS 2.0 direct na `id`, en deze app heeft het sinds
|
||||
# 0.1.1. Het hoort hier dus ook als verplicht: eruit halen breekt een installatie
|
||||
# die al verplaatst is.
|
||||
MANIFEST_ORDE = [
|
||||
"manifestVersion", "id", "category", "name", "version", "tagline",
|
||||
"manifestVersion", "id", "storage", "category", "name", "version", "tagline",
|
||||
"description", "releaseNotes", "developer", "website", "dependencies",
|
||||
"repo", "support", "port", "gallery", "path",
|
||||
]
|
||||
|
||||
@@ -41,7 +41,7 @@ services:
|
||||
# Bij een verhoging: eerst de digest hier wéghalen, dan bouwen en duwen, dan
|
||||
# de nieuwe erin. Een OUDE digest onder een NIEUWE tag levert stilzwijgend de
|
||||
# oude image.
|
||||
image: sc.kamenier-hamer.nl/sysop/atreides-assets:0.2.0@sha256:646fe7a169d9da0bad0df43e249237c88c7641d6c88f52cdd984b88c562d2859
|
||||
image: sc.kamenier-hamer.nl/sysop/atreides-assets:0.3.4@sha256:908afbc7895edfdf6c5dadd646edf85ced0053b4f5273d7372aa9080d00583a5
|
||||
restart: on-failure
|
||||
# Geen `user:` nodig: de image zet zelf `USER node`. Dat is anders dan bij
|
||||
# Evolu Relay, waar de image er geen heeft en de compose het per container
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 3.5 KiB |
@@ -7,45 +7,71 @@
|
||||
# voorgeschreven kop letterlijk goed en hoeft er bij inlevering alleen iets weg.
|
||||
manifestVersion: 1
|
||||
id: whatsnext-atreides-assets
|
||||
# umbrelOS 2.0: de datamap is verplaatsbaar naar een USB-schijf (ext4), niet naar
|
||||
# een netwerkshare. Letterlijk `data`, en het veld kan er niet meer uit. Zie
|
||||
# Umbrel-appstore-spec.md §7.
|
||||
storage:
|
||||
dataRoot: data
|
||||
category: finance
|
||||
name: Atreides Assets
|
||||
version: "0.2.1"
|
||||
version: "0.3.4"
|
||||
tagline: What you own, on your own machine
|
||||
description: >-
|
||||
A portfolio that nobody else gets to read. Every exchange dashboard and every tracking app
|
||||
answers to someone: it knows what you hold, what you bought it for, and when you moved it.
|
||||
This one runs on your Umbrel, keeps its database in your app folder, and talks to nothing
|
||||
you did not point it at.
|
||||
A bitcoin portfolio that stays on your own machine. It runs on your Umbrel, keeps its
|
||||
database in your app folder, and shares nothing you did not ask it to share.
|
||||
|
||||
|
||||
It tracks crypto and precious metals side by side, in one total. Balances come from two
|
||||
places and it keeps them apart on purpose: what a wallet actually holds on chain, and what
|
||||
your own bookkeeping says should be there. When those two drift, the app says so instead of
|
||||
picking a winner, and shows you the entries behind the difference.
|
||||
Track a bitcoin wallet and a bank account in one total, in the currency you choose. The app
|
||||
keeps two views of your balance apart on purpose: what the wallet holds on chain, and what
|
||||
your own bookkeeping says. When they drift, it shows you the entries behind the difference.
|
||||
|
||||
|
||||
Prices come from providers you choose and configure yourself, with your own API keys. There
|
||||
is no built-in account, no phone-home, and no analytics. The keys stay on the server side
|
||||
and never reach your browser.
|
||||
Prices come from Atreides Services. It sees which prices are asked for, never your holdings
|
||||
or amounts. There are no analytics and no tracking.
|
||||
|
||||
|
||||
What it does beyond a list of holdings: a wealth chart over any year you have data for, the
|
||||
figures you need for the Dutch box 3 declaration, signal charts for those who want them,
|
||||
and a dashboard you arrange yourself. A Lightning wallet can be connected over NWC or LND
|
||||
to read its balance.
|
||||
You get a wealth chart over every year you have data for, a dashboard you arrange yourself,
|
||||
and an AI connection over MCP: an assistant you already use can read your administration and
|
||||
propose entries, and nothing is written until you approve it.
|
||||
|
||||
|
||||
It also speaks MCP, so an AI assistant you already use can read the administration and
|
||||
propose entries. Proposals land in a queue and nothing is written until you approve it, and
|
||||
what the assistant was shown is logged per call.
|
||||
|
||||
|
||||
Your data is a directory: a database file and a secrets file per portfolio. Back it up,
|
||||
move it to another machine, or keep several administrations side by side and switch between
|
||||
them. There is no export you have to ask for.
|
||||
This is the free edition.
|
||||
releaseNotes: >-
|
||||
This update only brings a new app icon; the app itself is unchanged. Coming from 0.1.x? Then
|
||||
the notes for 0.2.0 below apply to you.
|
||||
0.3.4 upgrades the database on first start, so make sure your backup is recent. Wallets are now
|
||||
scanned by the app itself, every four hours, and the Update button is gone; you see a running scan in
|
||||
the status bar and on To do. To do looks at all open wallet movements together and proposes a booking
|
||||
for each, with how likely it is and why; what is certain can be booked in one go. Events can be linked
|
||||
into a chain, for example a purchase and the transfer that followed it, and a linked row shows a chain
|
||||
mark you can open. The AI connection now answers in the language of the app.
|
||||
|
||||
|
||||
0.3.3 reworks the Portfolios page: one card per portfolio with its assets, accounts, entries and
|
||||
last backup. A fresh install starts with a Demo portfolio next to your own, with thirty months of
|
||||
sample entries; it does not count towards the limits of the free edition. The free edition now
|
||||
allows any number of valuations. The licence page is reorganised, and the AI connection is off in a
|
||||
new portfolio until you turn it on. Price history and merging accounts move to the full edition.
|
||||
The space bar hides and shows amounts, the Return widget colours its amount, and wallet updates
|
||||
retry longer when mempool.space is busy.
|
||||
|
||||
|
||||
0.3.2 fixes what the first round on umbrelOS turned up. Daily prices now name their source in the
|
||||
week, month and year views. The onboarding waits until the start screen is gone, the limits of the
|
||||
free edition say what they count, and the Assets page no longer lists a currency that is only there
|
||||
for conversion. A failed wallet update now says why.
|
||||
|
||||
|
||||
0.3.1 is the free edition. Lightning, metals, more than one portfolio, the Dutch box 3
|
||||
figures, signal charts, import and export, Data and Health, and your own price providers
|
||||
move to the full edition. Your data stays where it is: nothing is deleted, and the full
|
||||
edition picks it up again.
|
||||
|
||||
|
||||
On umbrelOS 2.0 the app's storage can now be moved to a USB drive (ext4) in the app settings.
|
||||
Keep in mind that umbrelOS does not back up an app whose storage is on a USB drive. The app
|
||||
itself is unchanged. Coming from 0.1.x? Then the notes for 0.2.0 below apply to you.
|
||||
|
||||
|
||||
0.2.1 brought a new app icon.
|
||||
|
||||
|
||||
0.2.0: the first update with real features since the app came to umbrelOS. It upgrades the database
|
||||
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -8,9 +8,9 @@
|
||||
# in de root daar), net als bij Atreides Assets; deze store draagt alleen de
|
||||
# app-definitie.
|
||||
#
|
||||
# Geen `ports:` hier. De beheer-app is een web-UI en hangt volledig achter
|
||||
# app_proxy. De API voor afnemers krijgt later een eigen poort, en pas DIE wordt
|
||||
# eventueel gepubliceerd; daar staat nu nog niets voor.
|
||||
# De beheer-app is een web-UI en hangt volledig achter app_proxy, zonder eigen
|
||||
# poort. Alleen de API voor afnemers (host 3855) wordt gepubliceerd: die bedient een
|
||||
# cliënt zonder browsersessie, net als Gate en de Relay.
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
services:
|
||||
@@ -25,7 +25,8 @@ services:
|
||||
APP_PORT: 8090
|
||||
|
||||
server:
|
||||
# NOG NIET GEPIND: er is nog geen image geduwd. Na de eerste release zet
|
||||
# De digest hoort erbij en niet alleen de tag: een tag kan opnieuw geduwd
|
||||
# worden. Eerste release op 09-10-2026. Bij een volgende zet
|
||||
# `node tools/release-bump.mjs whatsnext-atreides-services <versie> <digest>`
|
||||
# de tag en de digest hier neer; dat script vervangt de hele regel en
|
||||
# controleert anoniem bij het register dat de digest onder die tag hangt.
|
||||
@@ -33,11 +34,27 @@ services:
|
||||
# DE TAG IS DE VERSIE VAN DE IMAGE EN NIET DIE VAN DE APP, net als bij Atreides
|
||||
# Assets: `version` in umbrel-app.yml moet bij élke wijziging omhoog, ook als
|
||||
# alleen dit bestand verandert.
|
||||
image: sc.kamenier-hamer.nl/sysop/atreides-services:0.0.1
|
||||
image: sc.kamenier-hamer.nl/sysop/atreides-services:0.0.32@sha256:8bc31df6eb7bcc334c76504f2538e252692caf69191b69f6bfbc54f1c89f0324
|
||||
restart: on-failure
|
||||
# Geen `user:` nodig: de image zet zelf `USER node` (uid 1000). Of die in een
|
||||
# door umbrelOS aangemaakte datamap mag schrijven, is nog niet bewezen; zie
|
||||
# de werkafspraken van UmbrelApps, sectie Atreides Assets.
|
||||
# De API voor afnemers, zoals Atreides Assets op de laptop of de Mac. Elke
|
||||
# aanroep behalve /v1/health vraagt een token, dat je op de pagina API van
|
||||
# de beheer-app aanmaakt. Zonder certificaat is het http, dus het token gaat
|
||||
# onversleuteld over het LAN; publiek hoort er een reverse proxy met TLS voor.
|
||||
# De beheer-app (8090) staat hier met opzet niet bij.
|
||||
#
|
||||
# Sinds image 0.0.10 antwoordt op deze poort ook de MCP-server voor een
|
||||
# AI-client, op /mcp, met tokens van een eigen soort (alleen lezen, of lezen
|
||||
# en wijzigen). Wie deze poort via een proxy publiceert, publiceert /mcp mee.
|
||||
#
|
||||
# 3855 op de host en niet 8091: dat laatste is een veelgebruikte poort en een
|
||||
# botsing merk je pas als de app niet start (dezelfde les als 3852 bij de Relay
|
||||
# en 50022 bij Gate). umbrelOS deelt hiervoor geen poort uit; `port` in het
|
||||
# manifest is de web-UI achter app_proxy. Binnen de container blijft het 8091.
|
||||
ports:
|
||||
- "3855:8091"
|
||||
# Geen `user:` nodig: de image zet zelf `USER node` (uid 1000). umbrelOS
|
||||
# maakt de datamap aan als `umbrel`, ook uid 1000, dus node mag erin
|
||||
# schrijven; gemeten op 09-10-2026.
|
||||
#
|
||||
# Geen healthcheck hier: die zit in de image (GET /api/health, ook zonder
|
||||
# login bereikbaar).
|
||||
@@ -48,6 +65,11 @@ services:
|
||||
# opstarten is daardoor verwacht. De netwerkgrens is hier de afwezigheid van
|
||||
# een `ports:`-regel plus de proxy.
|
||||
ATREIDES_SERVICES_HOST: "0.0.0.0"
|
||||
# Alleen om op de pagina Systeem het adres te tonen dat een afnemer invult,
|
||||
# zoals RELAY_PUBLIC_PORT bij de Relay. Moet gelijk zijn aan de hostpoort
|
||||
# bij `ports:` hierboven. Image 0.0.3 leest hem nog niet; dat doet pas de
|
||||
# eerste image na commit cff6bf4 in AtreidesServices. Tot dan doet hij niets.
|
||||
ATREIDES_SERVICES_API_PUBLIC_PORT: "3855"
|
||||
# HIER STAAT MET OPZET GEEN `ATREIDES_SERVICES_PASSWORD`. De login is
|
||||
# optioneel en wordt in de app zelf gezet, op de pagina Systeem. Een
|
||||
# wachtwoord uit de omgeving wint van dat scherm, en dan is het alleen nog
|
||||
@@ -56,4 +78,20 @@ services:
|
||||
# De datamap: db.sqlite, de minuutbestanden per maand en secrets.json. Een
|
||||
# koersreeks per moment is niet opnieuw op te halen, dus dit is de map die
|
||||
# in de back-up van umbrelOS hoort.
|
||||
#
|
||||
# Sinds `storage: dataRoot: data` in het manifest (0.0.3) herschrijft
|
||||
# umbrelOS 2.0 deze bron naar de plek die de gebruiker kiest, en zet hij
|
||||
# er `create_host_path: false` bij. Daarom moet data/app in de repo staan
|
||||
# (.gitkeep), en mag hier geen mount-optie bij die umbreld niet kent.
|
||||
# Staat de map op een USB-schijf, dan gaat hij NIET mee in de back-up.
|
||||
- ${APP_DATA_DIR}/data/app:/data
|
||||
# De standaard van het folderAccess-slot "backups" (umbrel-app.yml): zelfde
|
||||
# service en zelfde doelpad, en precies de map die de app zonder deze regel
|
||||
# ook zou gebruiken. Wijst de gebruiker het slot naar een NAS-map, dan
|
||||
# vervangt umbreld alleen deze mount; de rest van /data blijft lokaal.
|
||||
# Gemeten in het plan Netwerkopslag: een mount binnen /data verbergt de rest
|
||||
# niet.
|
||||
- ${APP_DATA_DIR}/data/app/backups:/data/backups
|
||||
# De standaard van het slot "price-history": de afgesloten maanden. Zonder
|
||||
# gekozen map dezelfde plek als zonder deze regel.
|
||||
- ${APP_DATA_DIR}/data/app/history:/data/history
|
||||
@@ -6,14 +6,47 @@
|
||||
# Wat de spec niet noemt, staat onderaan dit bestand en niet ertussen.
|
||||
manifestVersion: 1
|
||||
id: whatsnext-atreides-services
|
||||
# umbrelOS 2.0: de gebruiker kan de hele datamap (${APP_DATA_DIR}/data) in de
|
||||
# app-instellingen naar een USB-schijf verplaatsen. Alleen ext4 en geen
|
||||
# netwerkshare; die laatste weigert umbrelOS voor app-opslag, en de SQLite-
|
||||
# bestanden in WAL-stand horen er ook niet op. De waarde moet letterlijk `data`
|
||||
# zijn, en het veld kan er niet meer uit: een installatie die al verplaatst is,
|
||||
# start dan niet meer. Zie Umbrel-appstore-spec.md §7.
|
||||
storage:
|
||||
dataRoot: data
|
||||
# umbrelOS 2.0: een map die de gebruiker in de app-instellingen naar zijn
|
||||
# Home-map, een USB-schijf of een netwerkshare kan wijzen. Er wordt niets
|
||||
# gekopieerd; umbreld vervangt alleen de mount. De standaard is de mount op
|
||||
# dezelfde service en hetzelfde doelpad in docker-compose.yml, en die wijst naar
|
||||
# dezelfde plek als zonder dit slot, dus voor een bestaande installatie
|
||||
# verandert er niets. Sinds image 0.0.4 schrijft de app rechtstreeks in de
|
||||
# gekozen map, zonder eigen submap. "Backups" en "Price history" moeten dus elk
|
||||
# een eigen map krijgen: twee slots op dezelfde map weigert de app met de
|
||||
# melding "gedeeld".
|
||||
#
|
||||
# Het `id` is na de eerste release vast: umbrelOS bewaart de keuze van de
|
||||
# gebruiker per id, dus hernoemen gooit die weg. Op een netwerkshare valt de map
|
||||
# buiten de back-up van umbrelOS, en dat is hier juist de bedoeling.
|
||||
folderAccess:
|
||||
- id: backups
|
||||
name: Backups
|
||||
mounts:
|
||||
- service: server
|
||||
targetPath: /data/backups
|
||||
- id: price-history
|
||||
name: Price history
|
||||
mounts:
|
||||
- service: server
|
||||
targetPath: /data/history
|
||||
category: finance
|
||||
name: Atreides Services
|
||||
version: "0.0.1"
|
||||
version: "0.0.35"
|
||||
tagline: Your own price feed, recorded around the clock
|
||||
description: >-
|
||||
A price service that runs on your Umbrel and keeps what it records. It fetches prices day
|
||||
and night and stores every point with where it came from and when it was fetched. A price series per moment cannot be fetched again
|
||||
later, so this one is kept, in your app folder, and goes along with your Umbrel backup.
|
||||
and night and stores every point with where it came from and when it was fetched. A price
|
||||
series per moment cannot be fetched again later, so this one is kept, in your app folder,
|
||||
and goes along with your Umbrel backup.
|
||||
|
||||
|
||||
It is the companion of Atreides Assets: that app will read its prices from here instead
|
||||
@@ -23,17 +56,21 @@ description: >-
|
||||
|
||||
This is an early version. It records spot prices every minute from Bitvavo and
|
||||
mempool.space, and brings the administration app with its System page and an optional
|
||||
login of its own. The API for other apps follows in a later version.
|
||||
login of its own. Other apps read the prices through its API on port 3855, with a token
|
||||
you create on the API page.
|
||||
releaseNotes: >-
|
||||
First version in this store: spot prices every minute from Bitvavo and mempool.space, and
|
||||
the administration app. The API for other apps is not there yet.
|
||||
A new Sales page: sell Atreides Assets licences with Lightning invoices on your own LND node,
|
||||
with a macaroon that can only make and read invoices. Every installation of Atreides Assets can
|
||||
register with its licence for a token of its own, which only reads sources you allow to be
|
||||
passed on. The database is migrated to version 35 at the first start.
|
||||
developer: "WhatsNext?"
|
||||
website: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
|
||||
dependencies: []
|
||||
repo: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
|
||||
support: https://sc.kamenier-hamer.nl/sysop/UmbrelApps/issues
|
||||
# De poort waarop umbrelOS de beheer-app aanbiedt. 3850, 3851 en 3853 zijn van de
|
||||
# andere drie apps in deze store, 3852 is de eigen poort van de relay.
|
||||
# andere drie apps in deze store, 3852 is de eigen poort van de relay en 3855 die
|
||||
# van de API van deze app (docker-compose.yml).
|
||||
port: 3854
|
||||
# Leeg laten bij een nieuw pakket; de plaatjes doet het store-team zelf.
|
||||
gallery: []
|
||||
|
||||
@@ -7,9 +7,18 @@
|
||||
# voorgeschreven kop letterlijk goed en hoeft er bij inlevering alleen iets weg.
|
||||
manifestVersion: 1
|
||||
id: whatsnext-electrum-gate
|
||||
# umbrelOS 2.0: de datamap is verplaatsbaar naar een USB-schijf (ext4), niet naar
|
||||
# een netwerkshare. Letterlijk `data`, en het veld kan er niet meer uit. De
|
||||
# Zoraxy-map valt buiten data/ en blijft waar hij is. Zie
|
||||
# Umbrel-appstore-spec.md §7.
|
||||
storage:
|
||||
dataRoot: data
|
||||
category: bitcoin
|
||||
name: Electrum Gate
|
||||
version: "0.1.0"
|
||||
# LOOPT VOOR OP VERSION in tools/electrum-gate/build.sh (0.1.0) sinds 0.1.1, dat
|
||||
# alleen het manifest raakte. De volgende image wordt dus 0.1.2 en niet 0.1.1,
|
||||
# anders blijft dit nummer gelijk en rolt umbrelOS niets uit.
|
||||
version: "0.1.1"
|
||||
tagline: Your own node from anywhere, without waiting for Tor
|
||||
description: >-
|
||||
The privacy win is already yours: you run the Electrum server. A public one gets asked for
|
||||
@@ -56,6 +65,15 @@ description: >-
|
||||
# staan. In 0.0.9 stond er daardoor drie keer "Earlier releases" en twee keer
|
||||
# dezelfde 0.0.4-regel. De toets let er nu op.
|
||||
releaseNotes: >-
|
||||
On umbrelOS 2.0 the app's storage can now be moved to a USB drive (ext4) in the app settings. That
|
||||
folder holds your certificate choice, an uploaded certificate and the status the page reads; the
|
||||
certificates of your reverse proxy stay where they are. umbrelOS does not back up an app whose storage
|
||||
is on a USB drive. Nothing else changes.
|
||||
|
||||
|
||||
Everything in 0.1.0:
|
||||
|
||||
|
||||
The app now ships as its own image instead of mounting its program files into two stock images. Nothing
|
||||
changes in what it does; what changes is where the code lives. The app data folder now holds only your
|
||||
data: the certificate you chose, the one you uploaded, and the status the page reads. The startup logic
|
||||
|
||||
@@ -47,7 +47,8 @@ services:
|
||||
# De digest hoort erbij en niet alleen de tag: een tag kan opnieuw geduwd worden
|
||||
# en dan draait er iets anders dan hier staat. Staan ze allebei, dan bepaalt de
|
||||
# digest wat er gehaald wordt en is de tag alleen leesbaarheid. Houd de tag
|
||||
# gelijk aan VERSION in build.sh (drie keer hier) en `version` in het manifest.
|
||||
# gelijk aan VERSION in build.sh (drie keer hier). `version` in het manifest
|
||||
# loopt er sinds 0.7.2 op voor; zie de opmerking daar.
|
||||
#
|
||||
# De keerzijde bij een verhoging: een OUDE digest onder een NIEUWE tag levert
|
||||
# stilzwijgend de oude image. Bij 0.5.0 stond hier daarom even een tag zonder
|
||||
|
||||
@@ -9,9 +9,17 @@
|
||||
# andere app af. Een leeg veld zou suggereren dat er iets te kiezen valt.
|
||||
manifestVersion: 1
|
||||
id: whatsnext-evolu-relay
|
||||
# umbrelOS 2.0: de datamap is verplaatsbaar naar een USB-schijf (ext4), niet naar
|
||||
# een netwerkshare. Letterlijk `data`, en het veld kan er niet meer uit. Zie
|
||||
# Umbrel-appstore-spec.md §7.
|
||||
storage:
|
||||
dataRoot: data
|
||||
category: files
|
||||
name: Evolu Relay
|
||||
version: "0.7.1"
|
||||
# LOOPT VOOR OP VERSION in tools/evolu-relay/build.sh (0.7.1) sinds 0.7.2, dat
|
||||
# alleen het manifest raakte. De volgende image wordt dus 0.7.3 en niet 0.7.2,
|
||||
# anders blijft dit nummer gelijk en rolt umbrelOS niets uit.
|
||||
version: "0.7.2"
|
||||
tagline: Encrypted sync and backup for your local-first apps
|
||||
description: >-
|
||||
Local-first apps keep your data on your own device and work whether or not you have a
|
||||
@@ -43,6 +51,14 @@ description: >-
|
||||
your app at the address shown there. Away from home you will need a way in, such as Tailscale
|
||||
or a reverse proxy with your own domain.
|
||||
releaseNotes: >-
|
||||
On umbrelOS 2.0 the relay's storage can now be moved to a USB drive (ext4) in the app settings.
|
||||
Keep in mind that umbrelOS does not back up an app whose storage is on a USB drive, and that the
|
||||
relay holds the only copy in the middle. Nothing else changes.
|
||||
|
||||
|
||||
Everything in 0.7.1:
|
||||
|
||||
|
||||
Tidying up the two owner panels. They are called Accepted and Waiting now, the count of owners waiting
|
||||
sits in that panel's own heading instead of a widget at the top, the note under the relay address is
|
||||
gone, and a row on the waiting list no longer carries a "Waiting" tag: the panel it sits in already says
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,55 @@
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Schoolbord - huurders, afspraken en maandfacturen voor Schooltje Boschoord.
|
||||
#
|
||||
# Eén container: de Node-server serveert de app én bezit de database. Het recept
|
||||
# staat NIET in deze repo maar in `HomeGit/Schoolbord` (de Dockerfile in de root
|
||||
# daar), net als bij Atreides Assets en Atreides Services.
|
||||
#
|
||||
# Een web-UI met een EIGEN login (sinds image 0.0.6): hij hangt achter
|
||||
# app_proxy, zonder `ports:`, en is publiek bereikbaar via een eigen reverse
|
||||
# proxy op een subdomein. De inlog van umbrelOS staat er daarom niet meer voor.
|
||||
# Het beheerderswachtwoord maakt de app zelf, in /data/secrets.json.
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
services:
|
||||
# umbrelOS genereert deze service zelf; wij vullen alleen in waar hij heen moet
|
||||
# wijzen. De hostnaam heeft de vorm <app-id>_<compose-service>_1.
|
||||
# PROXY_AUTH_ADD "false": de app heeft een eigen login, en een bezoeker via het
|
||||
# subdomein heeft geen sessie bij umbrelOS.
|
||||
app_proxy:
|
||||
environment:
|
||||
APP_HOST: whatsnext-schoolbord_server_1
|
||||
APP_PORT: 8095
|
||||
PROXY_AUTH_ADD: "false"
|
||||
|
||||
server:
|
||||
# De digest hoort erbij en niet alleen de tag: een tag kan opnieuw geduwd
|
||||
# worden. Eerste release op 03-11-2026. Bij een volgende zet
|
||||
# `node tools/release-bump.mjs whatsnext-schoolbord <versie> <digest>` de tag
|
||||
# en de digest hier neer; dat script vervangt de hele regel en controleert
|
||||
# anoniem bij het register dat de digest onder die tag hangt.
|
||||
#
|
||||
# DE TAG IS DE VERSIE VAN DE IMAGE EN NIET DIE VAN DE APP: `version` in
|
||||
# umbrel-app.yml moet bij élke wijziging omhoog, ook als alleen dit bestand
|
||||
# verandert.
|
||||
image: sc.kamenier-hamer.nl/sysop/schoolbord:0.0.26@sha256:632b94ecf4fc9462b84e25879ed68e1257d429739f3c3ccd7c21cba06441a0f7
|
||||
restart: on-failure
|
||||
# Geen `user:` nodig: de image zet zelf `USER node` (uid 1000), en umbrelOS
|
||||
# maakt de datamap aan als `umbrel`, ook uid 1000 (gemeten bij Atreides
|
||||
# Services op 09-10-2026). Geen healthcheck hier: die zit in de image.
|
||||
environment:
|
||||
# ZONDER DIT IS DE APP ONBEREIKBAAR. De server bindt op 127.0.0.1, en in een
|
||||
# container is dat de loopback van de container: app_proxy komt er dan niet
|
||||
# bij. De netwerkgrens is hier de afwezigheid van `ports:` plus de proxy.
|
||||
SCHOOLBORD_HOST: "0.0.0.0"
|
||||
# Hoeveel proxy's er voor de app staan: de eigen reverse proxy en app_proxy.
|
||||
# Alleen dan gelooft de app X-Forwarded-For (de rem op inlogpogingen per
|
||||
# bezoeker) en X-Forwarded-Proto (Secure op het sessiecookie). Na te gaan
|
||||
# met een inlog van buitenaf; via het LAN staat er één proxy voor.
|
||||
SCHOOLBORD_TRUST_PROXY: "2"
|
||||
volumes:
|
||||
# De datamap: db.sqlite, de hele administratie. Dit is de map die in de
|
||||
# back-up van umbrelOS hoort. Met `storage: dataRoot: data` in het manifest
|
||||
# kan umbrelOS 2.0 hem naar een USB-schijf verplaatsen; daarom staat
|
||||
# data/app in de repo (.gitkeep) en staat hier geen mount-optie bij.
|
||||
- ${APP_DATA_DIR}/data/app:/data
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
@@ -0,0 +1,86 @@
|
||||
# De veldvolgorde hieronder is niet vrij: de packaging-documentatie van de
|
||||
# officiele appstore schrijft hem voor. Zie de tabel in
|
||||
# HomeGit/Docs/Referenties/UmbrelApps/Umbrel-appstore-spec.md, en de toets in
|
||||
# tests/test_appstore_vorm.py die hem vasthoudt.
|
||||
#
|
||||
# Wat de spec niet noemt, staat onderaan dit bestand en niet ertussen.
|
||||
manifestVersion: 1
|
||||
id: whatsnext-schoolbord
|
||||
# umbrelOS 2.0: de gebruiker kan de datamap naar een USB-schijf (ext4) verplaatsen.
|
||||
# Het veld kan er niet meer uit: een installatie die al verplaatst is, start dan
|
||||
# niet meer. Zie Umbrel-appstore-spec.md §7.
|
||||
storage:
|
||||
dataRoot: data
|
||||
category: finance
|
||||
name: Schoolbord
|
||||
version: "0.0.26"
|
||||
tagline: Tenants, agreements and monthly invoices for a small venue
|
||||
description: >-
|
||||
Schoolbord keeps the people of a small venue, the agreements made with them, and what follows
|
||||
from those: an agenda by month, week or day, with cancellations, moves and extra bookings. One
|
||||
button invoices a month: a PDF per tenant with a payment QR code (bunq.me, iDEAL), ready to share,
|
||||
send through WhatsApp or put in your mailbox as a draft. Notes with attachments, a report for the
|
||||
bookkeeping, and the agenda and rates delivered to the website.
|
||||
|
||||
|
||||
It has its own login and was made for Schooltje Boschoord; it speaks Dutch. Everything stays in your
|
||||
app folder on your Umbrel and goes along with your Umbrel backup.
|
||||
releaseNotes: >-
|
||||
No database step. The buttons of a selected row now wrap on a phone instead of running off the
|
||||
screen. Wijzigen always stands first, and two or more "Naar ..." buttons are folded into one button
|
||||
"Naar" with a menu. Coming from 0.0.24 or earlier, it also brings the following.
|
||||
On Afspraken, the energy surcharge next to its switch now says whether the heating
|
||||
season reaches the agreement at all ("geen toeslag: buiten het stookseizoen", or only in the season),
|
||||
so the switch can simply stay on; the invoice already counted only the days in the season. New on
|
||||
Personen and Afspraken: Proeffactuur, the invoice of a person for a month you choose, as invoicing
|
||||
would make it, shown as a PDF and never stored or numbered. Coming from 0.0.23 or earlier, it also
|
||||
brings the following, with a database step: make a backup first.
|
||||
New under Systeem, for every login: Eigen agenda, a
|
||||
private link that puts the agenda in your own calendar app (Google, Apple, Outlook), with only the
|
||||
moments you are part of or the whole agenda. It is off until you turn it on, works without logging
|
||||
in, and "Nieuwe link" makes the old one stop at once; use the address from outside, not the one at
|
||||
home. A basic login can now add, change and remove free agenda items, with Thermostaat and Website.
|
||||
On Overzicht the agenda pages back and forward by week or month. Coming from 0.0.22 or earlier, it
|
||||
also brings the following.
|
||||
Fixes two things on Afspraken. An agreement whose day part had later been made narrower could no
|
||||
longer be changed at all, not even its link or its thermostat ("De tijden vallen buiten ..."); now
|
||||
a narrower day part moves what no longer fits to the shortest day part it does fit, or none, and a
|
||||
database step repairs what already stood outside its part. And the switch Thermostaat in the edit
|
||||
row is saved again. On Personen, mailing a role is an envelope next to the plus, and a basic login
|
||||
sees a person's agreements and website defaults without invoices or amounts. Coming from 0.0.21 or
|
||||
earlier, it also brings the following, with two more database steps.
|
||||
What goes to the website is now clear
|
||||
everywhere: every person, agreement and moment on the agenda shows in its drawer whether it is on
|
||||
the website, why not, and where its title, link and price come from. The switch Website works per
|
||||
moment on the agenda, and a free agenda item can go to the website too. A cancelled moment is
|
||||
struck through on the site (needs the Agenda plugin 0.3.0), and a person set inactive is taken off.
|
||||
At the bottom of the menu a line says whether a change has reached the website yet. Persons now
|
||||
have a name and an optional company (shown as "Name (Company)"; the old contact person is turned
|
||||
into this, so check Personen after the update), a mobile number, and two more roles, volunteer and
|
||||
neighbour, with a draft mail to everyone of a role. Coming from 0.0.19 or earlier, it also carries
|
||||
the buttons "Naar ..." between related rows and Bekijken on Documenten.
|
||||
developer: "WhatsNext?"
|
||||
website: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
|
||||
dependencies: []
|
||||
repo: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
|
||||
support: https://sc.kamenier-hamer.nl/sysop/UmbrelApps/issues
|
||||
# De poort waarop umbrelOS de app aanbiedt. 3850 tot en met 3855 zijn van de
|
||||
# andere apps in deze store (3852 en 3855 als eigen poorten van de Relay en van
|
||||
# de API van Atreides Services).
|
||||
port: 3856
|
||||
# Leeg laten bij een nieuw pakket; de plaatjes doet het store-team zelf.
|
||||
gallery: []
|
||||
path: ""
|
||||
submitter: "WhatsNext?"
|
||||
submission: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
|
||||
|
||||
# ── Hieronder staat wat de voorgeschreven volgorde niet noemt ────────────────
|
||||
#
|
||||
# GEEN defaultPassword: de app maakt het beheerderswachtwoord zelf, bij de eerste
|
||||
# start, in /data/secrets.json. Een wachtwoord uit umbrelOS zou dat scherm
|
||||
# overrulen (de les van Atreides Assets 0.1.0).
|
||||
|
||||
# Het icoon hoort bij een eigen store en moet bij inlevering in de officiele
|
||||
# store juist weg: die host iconen apart. Daarom staat het als laatste. De boom
|
||||
# van het Schooltje op beige, van de website.
|
||||
icon: https://sc.kamenier-hamer.nl/sysop/UmbrelApps/raw/branch/main/whatsnext-schoolbord/icon.png
|
||||
Reference in new issue
Block a user