# Your first app in Crystal

> Write, build and run a small WolOS app in Crystal with Cryswol: it reads the system, tries to post a notification, and handles a refusal the way every app must.

Source: https://wolos.org/manual/first-app

## What it is

A WolOS app can be one file. This page walks through a twelve-line program in **Crystal**, the language WolOS apps are written in. It reads like a script and builds into a normal program. The program uses **Cryswol**, the Crystal library that speaks to WolOS for you: it opens a connection to the system, asks what the system is called and whether it is light or dark, and tries to post a notification. Think of Cryswol as the app's phone line to WolOS: the app asks, WolOS answers, and WolOS decides what the app may do.

## Why

WolOS gives apps one way in: a stable system library, and Cryswol on top of it for Crystal. An app never needs administrator rights to talk to the system, and it never reaches around the rules: before it does something personal, such as showing notifications, the person using it decides. For you as a developer that means no memory handling and no error codes: you get Crystal objects, and a refusal arrives as an error that carries WolOS's own sentence, ready to show to a person.

## How

**Before you start:** add the Developer module, which brings Crystal and Cryswol. See [Set up for development](/manual/developer-module).

1. **Make a folder for the project** in Terminal:

   ```sh
   mkdir -p ~/Work/hello && cd ~/Work/hello
   ```
2. **Write the program.** Open the editor (`code hello.cr`, or any editor) and save this as `hello.cr`:

   ```crystal
   require "cryswol"

   Wol::Context.open("org.example.hello") do |ctx|
     info = Wol::System.info(ctx)
     puts "Hello from #{info.os_name}, in #{info.appearance} mode"
     begin
       Wol::Notification.post(ctx, "Hello", "My first WolOS app")
       puts "Notification sent"
     rescue e : Wol::PermissionDenied
       puts e.message
     end
   end
   ```

   `Wol::Context.open` connects to WolOS under an *app id*, a name that is yours alone (here `org.example.hello`), and closes the connection when the block ends.
3. **Build it.** Crystal needs to know where the system's Crystal libraries live:

   ```sh
   CRYSTAL_PATH="/usr/lib/wolos/crystal:$(crystal env CRYSTAL_PATH)" crystal build hello.cr
   ```

   You see no output and a new program called `hello` in the folder. The first build takes a little while; later ones are quicker.

   ![Terminal in ~/Work/hello after the build: ls -l lists the new program hello, about 2.4 MB. Captured in a virtual machine running WolOS 0.12.0 with the Developer module.](/img/manual/vm-first-app-built.png)
4. **Run it:**

   ```sh
   ./hello
   ```

   You see *Hello from WolOS, in Light mode* (or Dark), printed by your program, and then WolOS's own sentence: *Notifications are off for hello. Turn them on in System Settings > Privacy & Security.* A new app may not show notifications until the person allows it, and the `rescue` turns that refusal into a line of text instead of a crash.

   ![Terminal: Hello from WolOS, in Light mode, then Notifications are off for hello. Turn them on in System Settings > Privacy & Security. Captured in a virtual machine running WolOS 0.12.0.](/img/manual/vm-first-app-denied.png)
5. **Look at Privacy & Security.** Open Settings > **Privacy & Security**.
   You see "No app has extra access" under Privacy: the pane lists apps that already hold a permission, and a brand-new app like `hello` has none. The program above shows the part every app needs anyway: a refusal arrives as an error with a sentence you can show to the person.

   ![Settings > Privacy & Security on a fresh WolOS 0.12.0: No app has extra access, Screen sharing, the list of what WolOS enforces, and Security (Firewall, System protections, Disk encryption, Startup Security). Captured in a virtual machine.](/img/manual/vm-privacy-no-app.png)

### What you just used

| Line | Does |
|---|---|
| `require "cryswol"` | Loads Cryswol. Everything is under the name `Wol`. |
| `Wol::Context.open(...) do \|ctx\|` | One connection to WolOS for this app, closed for you at the end. |
| `Wol::System.info(ctx)` | What the system is: its name, the kernel, light or dark, the accent colour. |
| `Wol::Notification.post(ctx, title, body)` | A notification, if the person allowed it. |
| `rescue e : Wol::PermissionDenied` | What to do when the person has not allowed it: here, print WolOS's sentence. |

Cryswol covers much more: windows, menus, the clipboard, saved preferences, open and save panels, power, network, sound, Search, messages between apps and background services. The [Cryswol page](/manual/cryswol-apps) lists every one with the permission it needs.

### Next: a window

For an app with a window, buttons and lists, follow [Your first Wolkit app](/reference/wolkit-tutorial). Wolkit is the WolOS kit for app windows; it is in preview.

## A worked example

Alice builds the twelve-line program and runs it. It prints the name of her system and whether it is light or dark, then asks to post a notification. A sheet asks her whether to allow it; she clicks Don't Allow. The program prints the sentence WolOS gave with the refusal and carries on without crashing, which is exactly how every app is meant to behave.

## If something goes wrong

| What you see | What to do |
|---|---|
| `crystal: command not found` | Add the Developer module: `wol modules install dev`. |
| `can't find file 'cryswol'` | The build line is missing `CRYSTAL_PATH`. Copy step 3 as it is. |
| `Unhandled exception: Notifications are off for hello.` | The program has no `rescue` for `Wol::PermissionDenied`. Add it as in step 2, and show the sentence to the person using your app. |
| `Wol::Unavailable` | The WolOS system service is not answering, for example in a plain console without a desktop session. Run the program from Terminal on the desktop. |
| `Wol::UsageError` | The program used something after its connection was closed, or outside the block it belongs to. This is a bug to fix in the code; it is never shown to a person. |
| The app starts here but not on another computer | It was built against a newer system library. When you package it, say which version it needs (`depends=(libwol>=1.N)`). |

## Related

[Set up for development](/manual/developer-module) · [Cryswol](/manual/cryswol-apps) · [Your first Wolkit app](/reference/wolkit-tutorial) · [Privacy and permissions](/manual/privacy) · [The terminal and the wol command](/manual/wol-basics)
