- CLI API - `panorama install` : Install a script into the database - `panorama show` : Show the information about a particular node, by ID - Database of nodes + code - Code - Code is all interface-based. Interface implementations give specific ways behaviors are implemented - Standard interfaces: - Indexed - Node definition ```typescript type Node = { metadata: KVStore, interfaces: Set, edges: Set<(EdgeType, Edge)>, } ``` - Within the metadata kv store, keys are namespaced, according to the application name. Application names must be registered ahead of time - Nodes implement interfaces, the code of which is fetched from the internet and validated - Nodes are namespaced, although namespacing isn't the primary hierarchy protocol. - This is needed because the database itself has a privileged namespace - The privileged namespace is for keeping track of database state, for example which scripts are trusted - Hooks: - Hooks consist of a (pattern, type, callback). The pattern is a metadata matcher, that determines if the node should be executed upon. The callback is Javascript code executed in the app's execution context. - Type determines what kind of callback to call. - Ensures callbacks: these make sure that a node has - Need to somehow figure out how to represent partially executed processes? i.e if a video indexer needs to take time to go through the video, the nodes should be available and committed, but somehow indicate that one of its processes isn't done - Maybe this should be a flag on the `NodeMeta` table - Database service: - IFTTT Events - Local vs. distributed IFTTT events? - Local means that ur particular device is repsonsible for running it - Distributed means any device can consume the event??? - is this even a good idea? Maybe just have one dedicated worker device. For example, may not want to run certain automation tasks on phone, while others may only be able to be run on phone - Cron tasks - Sync protocol: - After a modification, the modification gets written to a queue - CRDT? - Other privileged concepts: - User: a pub/priv key pair - Sample apps - Todos app - Metadata keys (these are somewhat based on Taskwarrior) - `status`: Enum{Pending | Deleted | Completed | Waiting | Recurring} - Task status - `description`: String - Description - `start`: DateTime - `due`: DateTime{.due >= .start} - `depends`: NodeRef - Interfaces: - `addTodo()` - Content / blob storage - Interfaces: - `Content` - `upload() -> Writer` - `get(node_id : NodeId) -> Reader` - `Writer` - `next_chunk(Vec)` - `Reader` - `next_chunk() -> Vec` - Personal journaling (like Logseq) - Weight / fitness tracking - Calendar app (store events) - Git app (store code) - File backup - Chat / Mail? - Photo viewer + indexing using ML models for face detection / keywords - Not sure yet: - Automatic sharing of database nodes? This seems like a bad idea for multiple reasons. Maybe better to just have a specific app for doing permission models - Other concerns - Local protection using libsecret? - Migrations? - If an application needs to migrate its data format, should we formalize this? - Data migrations that don't need to create / delete nodes, only metadata - Have the frontend also shipped with the backend? - Subset of React components to typecheck against - Aggregated queries and group by? Joins? - Middleware interface / higher-order nodes - the idea would be it would be solely dependent on / modify another - i think this would also want to pair with some notion of app-private nodes - for example, use cases would be: - version control? - update "hook" for this would create a third node and append it ``` const newPrev = new Node() newPrev.prev = curr.prev curr.prev = newPrev ``` creates like a linked list of version updates - Pub/Sub? - i think this could use the same interface as the query interface, so you would just "listen" to updates there, rather than just getting a single snapshot of it - findNodes interface: - Params - mode, one of "single", "many", "stream" - single: return a single document - many: return many documents (may limit with take) - TODO: what does a cursor look like? - stream: open a socket and keep listening for - streamType, one of "inserts" or "updates" - metadata: object - `{ [keyName] : { has?: boolean, value?: [value], select: boolean } }` - has means if it's not in there, then filter it out - value means that if the value's not exactly a match, then filter it out - could this include valueMatches, which runs a custom predicate? - select means should this be included in the result