This is a full reference on those functions and fields exposed by the kernel to processes and libraries. Firstly, it is important to note that a process runs within a coroutine. This allows a highly "traditional" form of mixing async and synchronous code with event-loop nesting and such designs. If this is not to your taste then you can just use one, not nested event loop. As it runs in a coroutine, events are received via coroutine.yield() - sandboxers beware! You may have to use coroutine.running() in order to successfully hide the implementation details of your sandbox (also events and potentially accesses headed in its direction...) An example KittenOS NEO program, solely using kernel APIs, that you will likely have to kill: neo.scheduleTimer(os.uptime() + 1) while true do local ev = coroutine.yield() if ev == "k.timer" then neo.emergency("Hello...") neo.scheduleTimer(os.uptime() + 1) end end This will say "Hello..." via the neo.emergency mechanism once every second, independently of anything else on the system. While this is obviously not a sane sys-init for actual use, if you have a disk that you can copy the kernel to and a copy of this, it might make a fun experiment. The way to exit the program is to return from your process's main function. The first field to note is: _VERSION: _VERSION from the host. The following are just wrapMeta'd host libraries (*: altered): math, table, string, unicode*, coroutine, os*, debug, utf8, bit32 unicode is extended with: safeTextFormat(s, p): Takes a string s, and a position p, (the position is optional, and is assumed to be 1 otherwise) and returns a space-padded string, with a space after each wide char to make unicode.len & co. act in screen units, along with the position translated. undoSafeTextFormat(s): Takes a string in padded-widechar format, and gets rid of the pad. Note that if padding is *missing*, wide characters become spaces. This leaves a string that's usually safe to pass to a GPU without any odd graphical glitches. The KittenOS NEO kernel also reserves the ability to take advantage of any full de-UTF16'd support for Unicode available on the system, but will not include such support as a shim for memory usage reasons. Programs that thus try to work around this problem should delegate this task to a library, in a separate package, which can then be updated as-needed if and when the issue is resolved. os is extended with: totalMemory = computer.totalMemory, freeMemory = computer.freeMemory, energy = computer.energy, maxEnergy = computer.maxEnergy, uptime = computer.uptime, address = computer.address The following are just host functions (*: wrapped for security - these functions detect metatable abuse): assert, ipairs, load, next*, pairs, pcall, xpcall, select, type, error, tonumber, tostring, setmetatable, getmetatable*, rawset*, rawget, rawlen, rawequal (NOTE: Before you consider that load has no checks: The policy regarding load is taken from the host system. Which means bytecode loading is almost certainly off. If it's not off, then this is the user's fault, as doing so is marked as a security risk for very obvious reasons. As for trying to use the environment to bypass a metatable, I tested. Metatables do apply to environments, including global creation. There is no way to win.) "require" and "neo" are the parts of the environment where a NEO-specific nature presents itself. require takes a string, and returns the value returned by the library at "libs/" .. str .. ".lua" on the primary disk. Since R2, the value is automatically wrapMeta'd, just in case. Before R2, libraries did this on their own, but this caused NEO-only code to crop up in libraries that did not need NEO-only code. The library name must be a valid path component, and the library path must also be valid - see ensurePathComponent, ensurePath for more info. The "neo" table is where most of the NEO-specificness is hiding, which is probably shown by its name. It is also where libraries differ to processes, as libraries get a subset of the table. For libraries, it contains: emergency: Equals ocemu.log, if available on the system. Else, NOP. readBufSize: The readBufSize kernel configuration value. Default: 2048. Adjusting this in the kernel allows adjusting how much the system will read at any given time, which can have non-obvious memory usage effects. Do note, following this limit is not a requirement and is not enforced - it's not a security matter, just optimization/memory. wrapMeta(v): A function that takes a value, and wraps it in such a way as to be immutable, returning the wrapped value. This is the first line of defense against memory use - by using this to protect a table, the result can be shared between untrusted code. listProcs(): A function that returns an ipairs-friendly process list. Values are: {pid, pkg, cpuUsageInSeconds} listApps(): Returns an ipairs-friendly list of applications on the system, like: {"app-test", "svc-liliput"} listLibs(): Returns an ipairs-friendly list of libraries on the system, such as: {"fmttext", "braille"} usAccessExists(s): Returns true if the specified access has been registered from userspace using the related "r." access. totalIdleTime(): Returns the current kernel idle time total, useful for measuring current CPU usage, and in turn comparing to application CPU time to get various statistics. ensurePath(s, root): Attempts to verify the safety of a path, and errors if any aspect seems incorrect. The root must be a prefix to the path, and the path must follow a strict standardized form that is guaranteed to always be supported and handled in the same way on any OC system. Essentially, "//" must not occur, and all "[^/]+" matches must be valid path components. ensurePathComponent(s): Ensures that a string is a safe filename via a character list and some special filename checks. UTF-8 characters are just flat out disallowed until someone can give me proof they won't blow up something somewhere. (This restriction is Windows's fault - I can't trust the encoding mess to not find some new and imaginative way of breaking filenames, so I'd rather that they get avoided until someone can try actually using them.) This does NOT ACCOUNT for *all* the Windows total nonsense (aux, com1) because if OC doesn't cover up that then you're kinda doomed. ensureType(v, ts): Checks that a value is of a given type, and errors otherwise. If the type is "table", it also errors if a metatable exists. The additional things available to processes are those things that require a process to use: pid: A field that specifies the process ID of this process. Harmless, but not entirely useful. pkg: A field that specifies the package name of this process. Useful if you're worried about your app getting renamed. dead: Actually a field, that isn't set at first, but is set later to indicate deadness. Useful if your process does anything that might lead to functions being called in the afterlife, such as providing an API. executeAsync: Function that takes an app name (aka: pkg), and a set of arguments to give it. NOTE: sys- apps cannot be started from non sys- apps no matter how hard you try, without k.root alterations to runProgramPolicy. Your process pkg and ID is prepended to the arguments. NOTE: This uses the result, err return format, except for security errors in which case it uses a full error, because you might just ignore the return value. A successful result is the PID. executeExt: Like executeAsync, but firstly, synchronous, and secondly, with an extra first parameter that contains a function to call on events encountered during the time. As for the return values, it tries to emulate os.execute, so it returns -1 & reason on load error, and 0 & death-reason otherwise. execute: executeExt, but with the first parameter set to a blank function. requestAccessAsync: A function that takes an access ID (aka 'perm') as a string (see kn-perms for info), and starts a security request that is responded to with a k.securityresponse such as: "k.securityresponse", perm, obj requestAccess(perm[, handler]): Runs requestAccessAsync, then sends events to handler (if any) while waiting for the response. sys-icecap is responsible for any automatic starting of services that may occur. requireAccess(perm, reason): requestAccess, but (perm, reason) - the reason is used in an error if the access cannot be gained. scheduleTimer: Given an os.uptime value, creates a timer and returns a completely meaningless table that is never touched by the kernel directly, called the "tag". The resulting event: "k.timer", tag, time These events are ONLY EVER sent as a consequence of this function, and this can be relied on safely. NOTE: Setting timers too far in the future has effects on system stability. So does using memory, and there's no way for me to stop that, either. So long as the timer is reached, alive or dead, things will work, but spamming timers has the consequence of memory use, and timers stick around after the process that owns them is dead. The list of events, tacked on at the end here: k.procnew(pkg, pid, ppkg, ppid): New process creation, with parent information (for seat tracking) This is not given to the process being created, as all of this gets given to it anyway on main function start. k.procdie(pkg, pid, reason, cpuTime): Process death. k.registration(uid): Registration of an access. k.deregistration(uid): Deregistration of an access. k.securityresponse(perm, obj): Response to a security request made with neo.requestAccess or such. k.timer(tag, time): A timer. Includes the planned uptime for comparison. h.*(...): Hardware signals, by type, such as "h.key_up" With that, I hope I have documented the kernel's interface to programs. -- This is released into the public domain. -- No warranty is provided, implied or otherwise.