Add your description here
Project description
AlarmDB is a memory-addressed, schema-driven row-based database backed by the iOS Clock app.
It can be interfaced via NyQL, a bespoke SQL-like query language.
Address space
We use a 5-bit architecture i.e. there are 32 possible addresses, from 0x00000 to 0x11111. Each address refers to a word of 32 bytes. Bytes within the word are referenced by their byte offset.
Conceptually, one memory address = one word = 32 bytes = one "row" of data in the database.
Encoding
An iOS alarm contains the following parameters:
Time(e.g."12:15 PM")Repeat Days(e.g.["Monday", "Friday"])Is Enabled("Yes"/"No")Allows Snooze("Yes"/"No")Label(arbitrary string)
Note that we could store all our data in the label, but that would be obviously cheating.
Addressing
There are 24*60=1440 possible alarm times, which works out to 10.49 bits. AlarmDB uses the 10 full (i.e. least significant) bits for addressing: the first 5 bits encode the address of the word (0-31), and the next 5 bits encode the byte offset within the word (0-31).
Encoding the data byte
To construct a single byte of data, we use the Repeat Days, Is Enabled, and Allows Snooze properties of an iOS alarm. The latter two are simple booleans i.e. one bit each.
Repeat Days would seem to be equally simple, i.e. each weekday is treated as a bit (on or off), which would give us seven bits of data. However, due to a bug in iOS Shortcuts, a Shortcut that tries to do anything with the Repeat Days of an alarm which has exactly one repeat day will cause the Shortcut to fail. For this reason, we are forced to sacrifice one bit (I chose the Lord's day, Sunday) to guard against this possibility. Essentially, if the remaining six bits (days) is going to have a popcount of 1, then the seventh bit (Sunday) flips on.
Since the Lord's day is exempt from doing work, we have six bits from Repeat Days and two bits from the other two booleans, which gives us one even byte.
Encoding the schema
Since we're only using the bottom 10 full bytes of the alarm address space, 0x10000000000-0x10110100000 i.e. 5:04PM-11:59PM is effectively "reserved" and we can use it to safely store the schema of the database separately from the data. In reality, only 0x10000000000-0x10000011111 (5:04PM-5:36PM) would be used because each record (word) has up to 32 bytes, so there can be at most 32 fields.
The bits of a field's byte are broken up into two parts:
- Bits 0-2: Data type
- Bits 3-7: Length in bytes minus 1 (i.e.
0x00000 -> 1and0x11111 -> 32)
Despite allowing up to 8 data types, AlarmDB currently only supports five:
TEXT(0x000): Parsed as UTF-8, truncated by null terminator (\x00)UINT(0x001): Parsed as an unsigned intINT(0x010): Parsed as a two's complement signed intTIMESTAMP(0x011): Parsed as an unsigned int and treated as a POSIX timestampBOOLEAN(0x100):falseif every bit is off,trueotherwise
Here I caved slightly and used the alarm's Label solely for the purpose of naming the field. There's a way to do it without it (encode the strings in the rest of the reserved address space with a max field name length of 12), but I thought of that too late and no longer have the energy to implement :)
Since the schema is user-defined, each record could be interpreted as a single 32-character string, 32 separate one-byte ints, or anything in between.
Querying: NyQL
Interfacing with AlarmDB is simple thanks to NyQL, a SQL-like query language. Below are some examples of NyQL queries (note the absence of a FROM clause because there is only one table):
-- Clears all rows and sets the schema for the table
-- Note that the total number of bytes cannot exceed 32
-- (column name, type, number of bytes)
SET SCHEMA
("alarm_name", TEXT, 8),
("repeat_bitmask", UINT, 1),
("hours", INT, 1),
("minutes", INT, 1),
("enabled", BOOLEAN, 1),
("allows_snooze", BOOLEAN, 1),
("timestamp_added", TIMESTAMP, 4);
-- Get the current schema
GET SCHEMA;
"""
Outputs ->
col_name,type,length_bytes
alarm_name,TEXT,16
repeat_bitmask,UINT,1
hours,INT,1
minutes,INT,1
enabled,BOOLEAN,1
allows_snooze,BOOLEAN,1
timestamp_added,TIMESTAMP,4
"""
-- Add rows
INSERT VALUES
("work", 62, 8, 15, TRUE, FALSE, 1778904247),
("do laundry", 8, 18, 0, TRUE, TRUE, 1778904372),
("write readme", 0, 19, 45, FALSE, TRUE, 1778905161);
-- Get all rows
SELECT *;
"""
Outputs ->
alarm_name,repeat_bitmask,hours,minutes,enabled,allows_snooze,timestamp_added
work,62,8,15,True,False,2026-05-16 00:04:07
do laundry,8,18,0,True,True,2026-05-16 00:06:12
write readme,0,19,45,False,True,2026-05-16 00:19:21
"""
-- More complex SELECT queries
SELECT AVG(hours) AS avg_hours, enabled GROUP BY enabled HAVING enabled;
"""
Outputs ->
avg_hours,enabled
13.0,True
"""
SELECT alarm_name AS morning_alarm_names WHERE hours < 12 AND enabled;
"""
Outputs ->
morning_alarm_names
work
"""
SELECT alarm_name, hours, minutes ORDER BY minutes DESC LIMIT 2;
"""
Outputs ->
alarm_name,hours,minutes
write readme,19,45
work,8,15
"""
-- Delete by condition
DELETE WHERE alarm_name = "write readme";
SELECT *;
"""
Outputs ->
alarm_name,repeat_bitmask,hours,minutes,enabled,allows_snooze,timestamp_added
work,62,8,15,True,False,2026-05-16 00:04:07
do laundry,8,18,0,True,True,2026-05-16 00:06:12
"""
-- Update by condition
UPDATE SET alarm_name = "pick up laundry", hours = 20 WHERE alarm_name = "do laundry";
SELECT *;
"""
Outputs ->
alarm_name,repeat_bitmask,hours,minutes,enabled,allows_snooze,timestamp_added
work,62,8,15,True,False,2026-05-16T00:04:07
pick up laundry,8,20,0,True,True,2026-05-16T00:06:12
"""
-- Set a new schema (this also wipes the database)
SET SCHEMA ("name", TEXT, 8), ("age", INT, 1);
Running AlarmDB
AlarmDB is easy-to-use, open-source, and free. To get started, download the following iOS shortcuts:
- AlarmDB write (called internally)
- AlarmDB read (called internally)
- AlarmDB (entrypoint)
You will need to update the cd command in the Run Shell Script action of the AlarmDB shortcut so it can navigate to your local copy of this repo.
It's convenient to add the AlarmDB shortcut to Quick Actions so that you can run NyQL anywhere by selecting the text and going Services > AlarmDB.
Recommended IDEs
- MacOS Notes app
Running on mobile
AlarmDB was developed on desktop, but it can be run on mobile if the Run Shell Script action of the AlarmDB shortcut is replaced with a Run Script Over SSH action.
Other notes
NULLs
There is no concept of a NULL value in AlarmDB. As long as there is at least one alarm (byte) in an address, it is assumed that the word at that address represents a full record. If there are no alarms in the byte offsets where the schema expects there to be, then they are assumed to be all zero. In other words, the "default" value for a missing int is 0, "" for missing strings, false for missing booleans, and January 1, 1970 for missing timestamps.
Serialization limitations
The AlarmDB read shortcut pipe-delimits alarm data, so | cannot be used in the name of any field in the schema (since those get written directly into the alarm's label).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file alarmdb-1.1.0.tar.gz.
File metadata
- Download URL: alarmdb-1.1.0.tar.gz
- Upload date:
- Size: 472.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f3fe80086b6cc4ac041110df2d08569a7877191bbad98d95a250520c4d9097bd
|
|
| MD5 |
af90de1cfd43a87584d32f727264ab43
|
|
| BLAKE2b-256 |
045342442649f98ec5c43fac358977263b8ffbf573ac9c651b269a7218f19b3c
|
Provenance
The following attestation bundles were made for alarmdb-1.1.0.tar.gz:
Publisher:
release.yml on marcos-acosta/alarmdb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
alarmdb-1.1.0.tar.gz -
Subject digest:
f3fe80086b6cc4ac041110df2d08569a7877191bbad98d95a250520c4d9097bd - Sigstore transparency entry: 1593571500
- Sigstore integration time:
-
Permalink:
marcos-acosta/alarmdb@0427b898144116309ef62c1af3b6f60388e5cff3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/marcos-acosta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0427b898144116309ef62c1af3b6f60388e5cff3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file alarmdb-1.1.0-py3-none-any.whl.
File metadata
- Download URL: alarmdb-1.1.0-py3-none-any.whl
- Upload date:
- Size: 16.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38853cd56737e1e9f2787372e270579b390c711f62eb9258f5f1f6bfa91dc54d
|
|
| MD5 |
44c7c052eae0fbeae439d032c010468f
|
|
| BLAKE2b-256 |
24ec5ee0cca993387afe3f4d41f63ab80e0b2cab0973084f8f7d49749c4b1d07
|
Provenance
The following attestation bundles were made for alarmdb-1.1.0-py3-none-any.whl:
Publisher:
release.yml on marcos-acosta/alarmdb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
alarmdb-1.1.0-py3-none-any.whl -
Subject digest:
38853cd56737e1e9f2787372e270579b390c711f62eb9258f5f1f6bfa91dc54d - Sigstore transparency entry: 1593571578
- Sigstore integration time:
-
Permalink:
marcos-acosta/alarmdb@0427b898144116309ef62c1af3b6f60388e5cff3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/marcos-acosta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0427b898144116309ef62c1af3b6f60388e5cff3 -
Trigger Event:
push
-
Statement type: