diff --git a/library/ntfy/Dockerfile b/library/ntfy/Dockerfile new file mode 100644 index 00000000..52010e3d --- /dev/null +++ b/library/ntfy/Dockerfile @@ -0,0 +1,29 @@ +FROM golang:1.21-alpine AS build + +ARG NTFY_VERSION=v2.11.0 + +WORKDIR /ntfy + +RUN apk add --no-cache git ca-certificates gcc musl-dev && \ + git clone --depth=1 --branch ${NTFY_VERSION} https://github.com/binwiederhier/ntfy.git /ntfy + +RUN mkdir -p server/docs server/site && \ + echo "Unikraft Ntfy Docs" > server/docs/index.html && \ + echo "Unikraft Ntfy Web" > server/site/index.html + +RUN CGO_ENABLED=1 go build \ + -buildmode=pie \ + -tags sqlite_omit_load_extension,osusergo,netgo \ + -trimpath \ + -ldflags="-linkmode=external -extldflags=-static-pie -s -w" \ + -o /usr/bin/ntfy \ + ./main.go + +RUN echo "127.0.0.1 localhost" > /tmp/hosts + +FROM scratch + +COPY --from=build /usr/bin/ntfy /usr/bin/ntfy +COPY --from=build /tmp/hosts /etc/hosts +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY ./server.yml /etc/ntfy/server.yml \ No newline at end of file diff --git a/library/ntfy/Kraftfile b/library/ntfy/Kraftfile new file mode 100644 index 00000000..6b15b039 --- /dev/null +++ b/library/ntfy/Kraftfile @@ -0,0 +1,109 @@ +spec: v0.6 + +name: ntfy + +rootfs: ./Dockerfile + +cmd: ["/usr/bin/ntfy", "serve", "-c", "/etc/ntfy/server.yml"] + +template: + source: https://github.com/unikraft/app-elfloader.git + version: staging + +unikraft: + source: https://github.com/unikraft/unikraft.git + version: staging + kconfig: + CONFIG_LIBUKBOOT_MAINTHREAD: 'y' + CONFIG_HAVE_PAGING_DIRECTMAP: 'y' + CONFIG_HAVE_PAGING: 'y' + CONFIG_PAGING: 'y' + + CONFIG_LIBUKVMEM: 'y' + CONFIG_LIBUKVMEM_DEFAULT_BASE: '0x0000001000000000' + CONFIG_LIBUKVMEM_DEMAND_PAGE_IN_SIZE: 12 + CONFIG_LIBUKVMEM_PAGEFAULT_HANDLER_PRIO: 4 + + CONFIG_LIBPOSIX_PROCESS: 'y' + CONFIG_LIBPOSIX_PROCESS_ARCH_PRCTL: 'y' + CONFIG_LIBPOSIX_PROCESS_MULTITHREADING: 'y' + CONFIG_LIBPOSIX_USER: 'y' + CONFIG_LIBPOSIX_USER_UID: 0 + CONFIG_LIBPOSIX_USER_GID: 0 + CONFIG_LIBPOSIX_ENVIRON: 'y' + + CONFIG_LIBPOSIX_FDIO: 'y' + CONFIG_LIBPOSIX_FDTAB: 'y' + CONFIG_LIBPOSIX_MMAP: 'y' + CONFIG_LIBPOSIX_PIPE: 'y' + CONFIG_LIBPOSIX_POLL: 'y' + CONFIG_LIBPOSIX_EVENTFD: 'y' + CONFIG_LIBPOSIX_TIME: 'y' + CONFIG_LIBPOSIX_TIMERFD: 'y' + CONFIG_LIBPOSIX_FUTEX: 'y' + CONFIG_LIBPOSIX_SYSINFO: 'y' + CONFIG_LIBPOSIX_SOCKET: 'y' + + CONFIG_LIBVFSCORE: 'y' + CONFIG_LIBVFSCORE_AUTOMOUNT: 'y' + CONFIG_LIBVFSCORE_AUTOMOUNT_CI: 'y' + CONFIG_LIBVFSCORE_AUTOMOUNT_CI_EINITRD: 'y' + CONFIG_LIBVFSCORE_AUTOMOUNT_UP: 'y' + CONFIG_LIBRAMFS: 'y' + CONFIG_LIBUK9P: 'y' + + CONFIG_APPELFLOADER_CUSTOMAPPNAME: 'y' + CONFIG_APPELFLOADER_BRK: 'y' + CONFIG_APPELFLOADER_VFSEXEC: 'y' + CONFIG_APPELFLOADER_VFSEXEC_EXECBIT: 'n' + CONFIG_APPELFLOADER_HFS: 'y' + CONFIG_APPELFLOADER_HFS_ETCRESOLVCONF: 'y' + CONFIG_APPELFLOADER_HFS_ETCHOSTS: 'y' + CONFIG_APPELFLOADER_HFS_ETCHOSTNAME: 'y' + CONFIG_APPELFLOADER_HFS_REPLACEEXIST: 'y' + + CONFIG_LIBUKRANDOM: 'y' + CONFIG_LIBUKRANDOM_GETRANDOM: 'y' + CONFIG_LIBUKRANDOM_DEVFS: 'y' + + CONFIG_UKPLAT_MEMREGION_MAX_COUNT: 128 + CONFIG_OPTIMIZE_DEADELIM: 'y' + CONFIG_OPTIMIZE_LTO: 'y' + CONFIG_LIBUKDEBUG: 'y' + CONFIG_LIBUKDEBUG_CRASH_SCREEN: 'y' + CONFIG_LIBUKDEBUG_ENABLE_ASSERT: 'y' + CONFIG_LIBUKDEBUG_PRINT_TIME: 'y' + CONFIG_LIBUKDEBUG_PRINTK: 'y' + CONFIG_LIBUKDEBUG_PRINTK_ERR: 'y' + +libraries: + lwip: + source: https://github.com/unikraft/lib-lwip.git + version: staging + kconfig: + CONFIG_LWIP_LOOPIF: 'y' + CONFIG_LWIP_UKNETDEV: 'y' + CONFIG_LWIP_LOOPBACK: 'y' + CONFIG_LWIP_THREADS: 'y' + CONFIG_LWIP_HEAP: 'y' + CONFIG_LWIP_IPV4: 'y' + CONFIG_LWIP_TCP: 'y' + CONFIG_LWIP_UDP: 'y' + CONFIG_LWIP_RAW: 'y' + CONFIG_LWIP_WND_SCALE: 'y' + CONFIG_LWIP_TCP_KEEPALIVE: 'y' + CONFIG_LWIP_SOCKET: 'y' + CONFIG_LWIP_AUTOIFACE: 'y' + CONFIG_LWIP_DHCP: 'y' + CONFIG_LWIP_DNS: 'y' + CONFIG_LWIP_ICMP: 'y' + CONFIG_LWIP_NUM_TCPCON: 64 + CONFIG_LWIP_NUM_TCPLISTENERS: 64 + + libelf: + source: https://github.com/unikraft/lib-libelf.git + version: staging + +targets: + - qemu/x86_64 + - fc/x86_64 \ No newline at end of file diff --git a/library/ntfy/README.md b/library/ntfy/README.md new file mode 100644 index 00000000..1f59c6dd --- /dev/null +++ b/library/ntfy/README.md @@ -0,0 +1,83 @@ +# Ntfy + +This directory contains the [ntfy](https://ntfy.sh/) runtime on Unikraft, in binary compatibility mode. +It implements a simple, HTTP-based pub-sub notification service that allows you to send push notifications to your phone or desktop via PUT/POST requests, running entirely on Unikraft. + +## Run and Use + +Use `kraft` to run the image and start a Unikraft instance: + +```bash +kraft run --rm -p 8080:80 -v /tmp:/tmp --plat qemu --arch x86_64 -M 256M unikraft.org/ntfy:latest +``` + +If the `--plat` argument is left out, it defaults to `qemu`. +If the `--arch` argument is left out, it defaults to your system's CPU architecture. + +Once executed, the Unikraft instance will listen internally on port `80` (mapped to `8080` on your host) and wait for connections. + +**Note:** To keep the Unikernel extremely lightweight and avoid massive Node.js/React dependencies, the graphical web UI is bypassed. You interact directly with the core Ntfy API via the terminal. + +To verify that the server is running and successfully processing messages, open a new terminal window and send a push notification to a topic (e.g., `test`) using `curl`: + +```bash +curl -d "Hello from Unikraft!" localhost:8080/test +``` + +You should immediately receive a JSON response from the Unikernel confirming that the payload was processed and saved, looking similar to this: + +```json +{"id":"EHmxKOCNDYre","time":1774517227,"expires":1774560427,"event":"message","topic":"test","message":"Hello from Unikraft!"} +``` + +## Inspect and Close + +To list information about the Unikraft instance, use: + +```bash +kraft ps -a +``` + +```text +NAME KERNEL ARGS CREATED STATUS MEM PORTS PLAT +kind_ozzie project://ntfy:qemu/x86_64 /usr/bin/ntfy serve -c /etc/ntfy/serv... 15 seconds ago running 256M 0.0.0.0:8080->80/tcp qemu/x86_64 +``` + +The instance name is `kind_ozzie`. +To close the Unikraft instance, close the `kraft` process (e.g., via `Ctrl+c`) or run: + +```bash +kraft rm kind_ozzie +``` + +## Build and Run Locally + +The commands so far used the pre-built Ntfy image available in the Unikraft registry. + +In order to build a local Ntfy image, clone this repository and `cd` into this directory. +Then use `kraft` to build an image locally: + +```bash +kraft build --no-cache --no-update --plat qemu --arch x86_64 +``` + +Similar to the `kraft run` command, if the `--plat` argument is left out, it defaults to `qemu`. +If the `--arch` argument is left out, it defaults to your system's CPU architecture. + +In order to run the locally built image, use `.` (*dot*, the current directory) as the final argument to the `kraft run` command: + +```bash +kraft run --rm -p 8080:80 -v /tmp:/tmp --plat qemu --arch x86_64 -M 256M . +``` + +Same as above, it will open port `8080` and wait for connections. + +## `kraft` and `sudo` + +Mixing invocations of `kraft` and `sudo` can lead to unexpected behavior. +Read more about how to start `kraft` without `sudo` at [https://unikraft.org/sudoless](https://unikraft.org/sudoless). + +## Learn More + +- [How to run unikernels locally](https://unikraft.org/docs/cli/running) +- [How to build `Dockerfile` root filesystems with BuildKit](https://unikraft.org/guides/building-dockerfile-images-with-buildkit) \ No newline at end of file diff --git a/library/ntfy/server.yml b/library/ntfy/server.yml new file mode 100644 index 00000000..dbfb2b9e --- /dev/null +++ b/library/ntfy/server.yml @@ -0,0 +1,4 @@ +base-url: "http://localhost" +listen-http: ":80" +cache-file: "/tmp/ntfy.db" +attachment-cache-dir: "/tmp/attachments" \ No newline at end of file