From 28839cf6468a0a502a23c57a1842ed0e0cecbc45 Mon Sep 17 00:00:00 2001 From: despiegk Date: Sun, 14 Sep 2025 07:19:52 +0200 Subject: [PATCH] ... --- aiprompts/instructions/herodb_base.md | 1440 +++++++++++++++++ lib/hero/herofs/README.md | 115 ++ lib/hero/herofs/specs.md | 289 ++++ lib/hero/heromodels copy/instructions.md | 31 - .../beta}/version_history.v | 0 .../openrpc/rpc_comment.v} | 0 6 files changed, 1844 insertions(+), 31 deletions(-) create mode 100644 aiprompts/instructions/herodb_base.md create mode 100644 lib/hero/herofs/README.md create mode 100644 lib/hero/herofs/specs.md delete mode 100644 lib/hero/heromodels copy/instructions.md rename lib/hero/{heromodels copy => heromodels/beta}/version_history.v (100%) rename lib/hero/{herohandlers/handler_comment.v => heromodels/openrpc/rpc_comment.v} (100%) diff --git a/aiprompts/instructions/herodb_base.md b/aiprompts/instructions/herodb_base.md new file mode 100644 index 00000000..eff9fac3 --- /dev/null +++ b/aiprompts/instructions/herodb_base.md @@ -0,0 +1,1440 @@ + +/Users/despiegk/code/github/incubaid/herolib +├── .github +│ └── workflows +├── .zed +├── aiprompts +│ ├── .openhands +│ ├── bizmodel +│ ├── documentor +│ ├── docusaurus +│ ├── herolib_advanced +│ ├── herolib_core +│ ├── instructions_archive +│ │ ├── models_from_v +│ │ └── processing +│ ├── v_advanced +│ ├── v_core +│ │ ├── array +│ │ ├── benchmark +│ │ ├── builtin +│ │ ├── crypto +│ │ ├── encoding +│ │ ├── io +│ │ ├── json +│ │ ├── json2 +│ │ ├── maps +│ │ ├── net +│ │ ├── orm +│ │ ├── regex +│ │ ├── string +│ │ ├── time +│ │ ├── toml +│ │ └── veb +│ └── v_veb_webserver +├── cli +├── docker +│ ├── herolib +│ │ └── scripts +│ └── postgresql +├── examples +│ ├── aiexamples +│ ├── biztools +│ │ ├── _archive +│ │ ├── bizmodel_docusaurus +│ │ │ └── archive +│ │ │ └── img +│ │ └── examples +│ │ └── full +│ ├── builder +│ │ └── remote_executor +│ ├── clients +│ ├── core +│ │ ├── base +│ │ ├── db +│ │ ├── logger +│ │ ├── openapi +│ │ │ └── gitea +│ │ ├── openrpc +│ │ │ └── examples +│ │ │ ├── openrpc_client +│ │ │ ├── openrpc_docs +│ │ │ └── petstore_client +│ │ └── pathlib +│ │ └── examples +│ │ ├── list +│ │ ├── md5 +│ │ ├── scanner +│ │ └── sha256 +│ ├── data +│ │ ├── location +│ │ ├── ourdb_syncer +│ │ ├── params +│ │ │ ├── args +│ │ │ │ └── data +│ │ │ └── paramsfilter +│ │ └── resp +│ ├── develop +│ │ ├── codewalker +│ │ ├── gittools +│ │ ├── heroprompt +│ │ ├── ipapi +│ │ ├── juggler +│ │ │ └── hero +│ │ │ └── playbook +│ │ ├── luadns +│ │ ├── openai +│ │ ├── runpod +│ │ ├── vastai +│ │ └── wireguard +│ ├── hero +│ │ ├── db +│ │ ├── generation +│ │ │ ├── blank_generation +│ │ │ └── openapi_generation +│ │ │ └── example_actor +│ │ │ └── specs +│ │ ├── herofs +│ │ ├── heromodels +│ │ └── openapi +│ │ └── data +│ ├── installers +│ │ ├── db +│ │ ├── infra +│ │ ├── lang +│ │ ├── net +│ │ ├── sysadmintools +│ │ ├── threefold +│ │ └── virt +│ ├── installers_remote +│ ├── jobs +│ ├── lang +│ │ └── python +│ ├── mcp +│ │ ├── http_demo +│ │ ├── http_server +│ │ ├── inspector +│ │ └── simple_http +│ ├── osal +│ │ ├── coredns +│ │ ├── download +│ │ ├── ping +│ │ ├── process +│ │ │ ├── process_bash +│ │ │ └── process_python +│ │ ├── rsync +│ │ ├── sandbox +│ │ │ └── examples +│ │ ├── sshagent +│ │ ├── tmux +│ │ │ └── heroscripts +│ │ ├── ubuntu +│ │ └── zinit +│ │ ├── rpc +│ │ └── simple +│ ├── schemas +│ │ ├── example +│ │ │ └── testdata +│ │ ├── openapi +│ │ │ └── codegen +│ │ └── openrpc +│ ├── sshagent +│ ├── threefold +│ │ ├── grid +│ │ │ ├── deploy +│ │ │ └── utils +│ │ ├── gridproxy +│ │ ├── holochain +│ │ ├── incatokens +│ │ │ └── data +│ │ ├── solana +│ │ └── tfgrid3deployer +│ │ ├── gw_over_wireguard +│ │ ├── heroscript +│ │ ├── hetzner +│ │ ├── open_webui_gw +│ │ └── vm_gw_caddy +│ ├── tools +│ │ └── imagemagick +│ │ └── .backup +│ ├── ui +│ │ ├── console +│ │ │ ├── console2 +│ │ │ └── flow1 +│ │ └── telegram +│ ├── vfs +│ │ └── vfs_db +│ ├── virt +│ │ ├── daguserver +│ │ ├── docker +│ │ │ └── ai_web_ui +│ │ ├── heropods +│ │ ├── hetzner +│ │ ├── lima +│ │ ├── podman +│ │ └── windows +│ ├── web +│ │ ├── doctree +│ │ │ └── content +│ │ └── markdown_renderer +│ └── webdav +├── lib +│ ├── ai +│ │ ├── escalayer +│ │ ├── mcp +│ │ │ ├── baobab +│ │ │ ├── cmd +│ │ │ ├── mcpgen +│ │ │ │ ├── schemas +│ │ │ │ └── templates +│ │ │ ├── pugconvert +│ │ │ │ ├── cmd +│ │ │ │ ├── logic +│ │ │ │ │ └── templates +│ │ │ │ └── mcp +│ │ │ ├── rhai +│ │ │ │ ├── cmd +│ │ │ │ ├── example +│ │ │ │ ├── logic +│ │ │ │ │ ├── prompts +│ │ │ │ │ └── templates +│ │ │ │ └── mcp +│ │ │ ├── rust +│ │ │ └── vcode +│ │ │ ├── cmd +│ │ │ ├── logic +│ │ │ └── mcp +│ │ └── utils +│ ├── biz +│ │ ├── bizmodel +│ │ │ ├── docu +│ │ │ ├── exampledata +│ │ │ └── templates +│ │ ├── investortool +│ │ │ └── simulator +│ │ │ └── templates +│ │ ├── planner +│ │ │ ├── examples +│ │ │ └── models +│ │ └── spreadsheet +│ │ └── docu +│ ├── builder +│ ├── clients +│ │ ├── giteaclient +│ │ ├── ipapi +│ │ ├── jina +│ │ │ └── py_specs +│ │ ├── livekit +│ │ ├── mailclient +│ │ ├── meilisearch +│ │ ├── mycelium +│ │ ├── mycelium_rpc +│ │ ├── openai +│ │ │ ├── audio +│ │ │ ├── embeddings +│ │ │ ├── files +│ │ │ ├── finetune +│ │ │ ├── images +│ │ │ └── moderation +│ │ ├── postgresql_client +│ │ ├── qdrant +│ │ ├── rclone +│ │ ├── runpod +│ │ ├── sendgrid +│ │ ├── traefik +│ │ ├── vastai +│ │ ├── wireguard +│ │ ├── zerodb_client +│ │ └── zinit +│ ├── conversiontools +│ │ ├── docsorter +│ │ │ └── pythonscripts +│ │ ├── imagemagick +│ │ ├── pdftotext +│ │ └── text_extractor +│ ├── core +│ │ ├── base +│ │ ├── code +│ │ │ └── templates +│ │ │ ├── comment +│ │ │ ├── function +│ │ │ ├── interface +│ │ │ └── struct +│ │ ├── generator +│ │ │ └── generic +│ │ │ └── templates +│ │ ├── herocmds +│ │ ├── httpconnection +│ │ ├── logger +│ │ ├── openrpc_remove +│ │ │ ├── examples +│ │ │ └── specs +│ │ ├── pathlib +│ │ ├── playbook +│ │ ├── playcmds +│ │ ├── playmacros +│ │ ├── redisclient +│ │ ├── rootpath +│ │ ├── smartid +│ │ ├── texttools +│ │ │ └── regext +│ │ │ └── testdata +│ │ └── vexecutor +│ ├── crypt +│ │ ├── aes_symmetric +│ │ ├── crpgp +│ │ ├── ed25519 +│ │ ├── keychain +│ │ ├── keysafe +│ │ ├── openssl +│ │ ├── pgp +│ │ └── secrets +│ ├── data +│ │ ├── cache +│ │ ├── countries +│ │ │ └── data +│ │ ├── currency +│ │ ├── dbfs +│ │ ├── dedupestor +│ │ │ └── dedupe_ourdb +│ │ ├── doctree +│ │ │ ├── collection +│ │ │ │ ├── data +│ │ │ │ ├── template +│ │ │ │ └── testdata +│ │ │ │ └── export_test +│ │ │ │ ├── export_expected +│ │ │ │ │ └── src +│ │ │ │ │ └── col1 +│ │ │ │ │ └── img +│ │ │ │ └── mytree +│ │ │ │ └── dir1 +│ │ │ │ └── dir2 +│ │ │ ├── pointer +│ │ │ └── testdata +│ │ │ ├── actions +│ │ │ │ └── functionality +│ │ │ ├── export_test +│ │ │ │ ├── export_expected +│ │ │ │ │ ├── col1 +│ │ │ │ │ │ └── img +│ │ │ │ │ └── col2 +│ │ │ │ └── mytree +│ │ │ │ ├── dir1 +│ │ │ │ │ └── dir2 +│ │ │ │ └── dir3 +│ │ │ ├── process_defs_test +│ │ │ │ ├── col1 +│ │ │ │ └── col2 +│ │ │ ├── process_includes_test +│ │ │ │ ├── col1 +│ │ │ │ └── col2 +│ │ │ ├── rpc +│ │ │ └── tree_test +│ │ │ ├── fruits +│ │ │ │ └── berries +│ │ │ │ └── img +│ │ │ └── vegetables +│ │ │ └── cruciferous +│ │ ├── encoder +│ │ ├── encoderhero +│ │ ├── flist +│ │ ├── gid +│ │ ├── graphdb +│ │ ├── ipaddress +│ │ ├── location +│ │ ├── markdown +│ │ │ ├── elements +│ │ │ ├── parsers +│ │ │ ├── testdata +│ │ │ └── tools +│ │ ├── markdownparser2 +│ │ ├── markdownrenderer +│ │ ├── mnemonic +│ │ ├── models +│ │ │ └── hr +│ │ ├── ourdb +│ │ ├── ourdb_syncer +│ │ │ ├── http +│ │ │ └── streamer +│ │ ├── ourjson +│ │ ├── ourtime +│ │ ├── paramsparser +│ │ ├── radixtree +│ │ ├── resp +│ │ ├── serializers +│ │ ├── tst +│ │ ├── verasure +│ │ └── vstor +│ ├── dav +│ │ └── webdav +│ │ ├── bin +│ │ ├── specs +│ │ └── templates +│ ├── develop +│ │ ├── codewalker +│ │ ├── gittools +│ │ │ └── tests +│ │ ├── heroprompt +│ │ │ └── templates +│ │ ├── luadns +│ │ ├── performance +│ │ │ └── cmd +│ │ ├── sourcetree +│ │ ├── vscode +│ │ └── vscode_extensions +│ │ └── ourdb +│ │ └── templates +│ ├── hero +│ │ ├── db +│ │ ├── herocluster +│ │ │ └── example +│ │ ├── herofs +│ │ ├── herohandlers +│ │ ├── heromodels +│ │ └── heromodels copy +│ │ └── examples +│ ├── installers +│ │ ├── base +│ │ │ └── templates +│ │ ├── db +│ │ │ ├── cometbft +│ │ │ │ └── templates +│ │ │ ├── meilisearch_installer +│ │ │ ├── postgresql +│ │ │ │ └── templates +│ │ │ ├── qdrant_installer +│ │ │ │ └── templates +│ │ │ ├── zerodb +│ │ │ └── zerofs +│ │ ├── develapps +│ │ │ ├── chrome +│ │ │ └── vscode +│ │ ├── infra +│ │ │ ├── coredns +│ │ │ │ └── templates +│ │ │ ├── gitea +│ │ │ │ └── templates +│ │ │ ├── livekit +│ │ │ │ └── templates +│ │ │ └── zinit_installer +│ │ ├── lang +│ │ │ ├── golang +│ │ │ ├── herolib +│ │ │ ├── nodejs +│ │ │ ├── python +│ │ │ ├── rust +│ │ │ └── vlang +│ │ ├── net +│ │ │ ├── mycelium_installer +│ │ │ ├── wireguard_installer +│ │ │ └── yggdrasil +│ │ ├── sysadmintools +│ │ │ ├── actrunner +│ │ │ │ └── templates +│ │ │ ├── b2 +│ │ │ ├── fungistor +│ │ │ ├── garage_s3 +│ │ │ │ └── templates +│ │ │ ├── grafana +│ │ │ ├── prometheus +│ │ │ │ └── templates +│ │ │ ├── rclone +│ │ │ │ └── templates +│ │ │ ├── restic +│ │ │ └── s3 +│ │ ├── threefold +│ │ │ ├── griddriver +│ │ │ └── tfrobot +│ │ ├── ulist +│ │ ├── virt +│ │ │ ├── cloudhypervisor +│ │ │ ├── docker +│ │ │ ├── herorunner +│ │ │ ├── lima +│ │ │ │ └── templates +│ │ │ ├── pacman +│ │ │ │ └── templates +│ │ │ ├── podman +│ │ │ ├── qemu +│ │ │ └── youki +│ │ └── web +│ │ ├── bun +│ │ ├── imagemagick +│ │ ├── lighttpd +│ │ │ └── templates +│ │ ├── tailwind +│ │ ├── tailwind4 +│ │ ├── traefik +│ │ │ └── templates +│ │ └── zola +│ ├── lang +│ │ ├── python +│ │ │ └── templates +│ │ └── rust +│ ├── mcp +│ │ ├── baobab +│ │ ├── cmd +│ │ ├── mcpgen +│ │ │ ├── schemas +│ │ │ └── templates +│ │ ├── pugconvert +│ │ │ ├── cmd +│ │ │ ├── logic +│ │ │ │ └── templates +│ │ │ └── mcp +│ │ ├── rhai +│ │ │ ├── cmd +│ │ │ ├── example +│ │ │ ├── logic +│ │ │ │ ├── prompts +│ │ │ │ └── templates +│ │ │ └── mcp +│ │ ├── transport +│ │ └── vcode +│ │ ├── cmd +│ │ ├── logic +│ │ └── mcp +│ ├── osal +│ │ ├── core +│ │ ├── coredns +│ │ ├── hostsfile +│ │ ├── linux +│ │ │ └── templates +│ │ ├── netns +│ │ ├── notifier +│ │ ├── osinstaller +│ │ ├── rsync +│ │ │ └── templates +│ │ ├── screen +│ │ ├── sshagent +│ │ ├── startupmanager +│ │ ├── systemd +│ │ │ └── templates +│ │ ├── tmux +│ │ │ └── bin +│ │ ├── traefik +│ │ │ └── specs +│ │ ├── tun +│ │ ├── ubuntu +│ │ └── ufw +│ ├── schemas +│ │ ├── jsonrpc +│ │ │ ├── reflection +│ │ │ └── testdata +│ │ │ ├── testmodule +│ │ │ └── testserver +│ │ ├── jsonschema +│ │ │ ├── codegen +│ │ │ │ └── templates +│ │ │ └── testdata +│ │ ├── openapi +│ │ │ ├── codegen +│ │ │ ├── templates +│ │ │ └── testdata +│ │ └── openrpc +│ │ ├── codegen +│ │ │ ├── templates +│ │ │ └── testdata +│ │ ├── server +│ │ └── testdata +│ │ └── petstore_client +│ ├── security +│ │ ├── authentication +│ │ │ └── templates +│ │ └── jwt +│ ├── threefold +│ │ ├── grid3 +│ │ │ ├── deploy_tosort +│ │ │ ├── deployer +│ │ │ ├── deployer2_sort +│ │ │ ├── griddriver +│ │ │ ├── gridproxy +│ │ │ │ └── model +│ │ │ ├── models +│ │ │ ├── rmb +│ │ │ ├── tfrobot +│ │ │ │ └── templates +│ │ │ ├── tokens +│ │ │ └── zerohub +│ │ ├── grid4 +│ │ │ ├── datamodel +│ │ │ ├── datamodelsimulator +│ │ │ ├── farmingsimulator +│ │ │ │ └── templates +│ │ │ └── gridsimulator +│ │ │ └── manual +│ │ ├── incatokens +│ │ │ └── templates +│ │ └── models +│ │ ├── business +│ │ ├── core +│ │ ├── finance +│ │ ├── flow +│ │ ├── identity +│ │ ├── legal +│ │ ├── library +│ │ ├── location +│ │ └── payment +│ ├── ui +│ │ ├── console +│ │ ├── generic +│ │ ├── logger +│ │ ├── telegram +│ │ │ └── client +│ │ ├── template +│ │ └── uimodel +│ ├── vfs +│ │ ├── vfs_calendar +│ │ ├── vfs_contacts +│ │ ├── vfs_db +│ │ ├── vfs_local +│ │ ├── vfs_mail +│ │ └── vfs_nested +│ ├── virt +│ │ ├── cloudhypervisor +│ │ ├── crun +│ │ ├── docker +│ │ ├── heropods +│ │ ├── herorun +│ │ ├── herorun2 +│ │ ├── hetznermanager +│ │ ├── lima +│ │ │ ├── raw +│ │ │ └── templates +│ │ ├── podman +│ │ └── qemu +│ │ └── templates +│ └── web +│ ├── doctreeclient +│ ├── docusaurus +│ │ └── example +│ ├── echarts +│ ├── site +│ │ └── example +│ └── ui +│ ├── static +│ │ ├── css +│ │ └── js +│ └── templates +│ └── admin +├── libarchive +│ ├── baobab +│ │ ├── actor +│ │ ├── generator +│ │ │ ├── _archive +│ │ │ ├── templates +│ │ │ └── testdata +│ │ ├── osis +│ │ ├── specification +│ │ └── stage +│ │ └── interfaces +│ ├── buildah +│ ├── daguserver +│ │ └── templates +│ ├── dify +│ │ └── templates +│ ├── examples +│ │ └── baobab +│ │ ├── generator +│ │ │ ├── basic +│ │ │ ├── geomind_poc +│ │ │ └── openapi_e2e +│ │ └── specification +│ ├── installers +│ │ └── web +│ │ └── caddy2 +│ │ └── templates +│ ├── rhai +│ │ ├── prompts +│ │ ├── templates +│ │ └── testdata +│ ├── starlight +│ │ └── templates +│ └── zinit +│ └── zinit +├── manual +│ ├── best_practices +│ │ ├── osal +│ │ └── scripts +│ ├── core +│ │ └── concepts +│ ├── documentation +│ └── playcmds +├── research +│ ├── globals +│ └── openrpc +├── tests +│ └── data +└── vscodeplugin + └── heroscrypt-syntax + └── syntaxes + + + + +File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/ai_instructions.md +```md +# HeroDB Model Creation Instructions for AI + +## Overview + +This document provides clear instructions for AI agents to create new HeroDB models similar to `comment.v`. These models are used to store structured data in Redis using the HeroDB system. + +## Key Concepts + +- Each model represents a data type stored in Redis hash sets +- Models must implement serialization/deserialization using the `encoder` module +- Models inherit from the `Base` struct which provides common fields +- The database uses a factory pattern for model access + +## File Structure + +Create a new file in `lib/hero/heromodels/` with the model name (e.g., `calendar.v`). + +## Required Components + +### 1. Model Struct Definition + +Define your model struct with the following pattern: + +```v +@[heap] +pub struct Calendar { + db.Base // Inherit from Base struct +pub mut: + // Add your specific fields here + title string + start_time i64 + end_time i64 + location string + attendees []string +} +``` + +### 2. Type Name Method + +Implement a method to return the model's type name: + +```v +pub fn (self Calendar) type_name() string { + return 'calendar' +} +``` + +### 3. Serialization (dump) Method + +Implement the `dump` method to serialize your struct's fields using the encoder: + +```v +pub fn (self Calendar) dump(mut e &encoder.Encoder) ! { + e.add_string(self.title) + e.add_i64(self.start_time) + e.add_i64(self.end_time) + e.add_string(self.location) + e.add_list_string(self.attendees) +} +``` + +### 4. Deserialization (load) Method + +Implement the `load` method to deserialize your struct's fields: + +```v +fn (mut self DBCalendar) load(mut o Calendar, mut e &encoder.Decoder) ! { + o.title = e.get_string()! + o.start_time = e.get_i64()! + o.end_time = e.get_i64()! + o.location = e.get_string()! + o.attendees = e.get_list_string()! +} +``` + +### 5. Model Arguments Struct + +Define a struct for creating new instances of your model: + +```v +@[params] +pub struct CalendarArg { +pub mut: + title string @[required] + start_time i64 + end_time i64 + location string + attendees []string +} +``` + +### 6. Database Wrapper Struct + +Create a database wrapper struct for your model: + +```v +pub struct DBCalendar { +pub mut: + db &db.DB @[skip; str: skip] +} +``` + +### 7. Factory Integration + +Add your model to the ModelsFactory struct in `factory.v`: + +```v +pub struct ModelsFactory { +pub mut: + comments DBCalendar + // ... other models +} +``` + +And initialize it in the `new()` function: + +```v +pub fn new() !ModelsFactory { + mut mydb := db.new()! + return ModelsFactory{ + comments: DBCalendar{ + db: &mydb + } + // ... initialize other models + } +} +``` + +## Encoder Methods Reference + +Use these methods for serialization/deserialization: + +### Encoder (Serialization) +- `e.add_bool(val bool)` +- `e.add_u8(val u8)` +- `e.add_u16(val u16)` +- `e.add_u32(val u32)` +- `e.add_u64(val u64)` +- `e.add_i8(val i8)` +- `e.add_i16(val i16)` +- `e.add_i32(val i32)` +- `e.add_i64(val i64)` +- `e.add_f32(val f32)` +- `e.add_f64(val f64)` +- `e.add_string(val string)` +- `e.add_list_bool(val []bool)` +- `e.add_list_u8(val []u8)` +- `e.add_list_u16(val []u16)` +- `e.add_list_u32(val []u32)` +- `e.add_list_u64(val []u64)` +- `e.add_list_i8(val []i8)` +- `e.add_list_i16(val []i16)` +- `e.add_list_i32(val []i32)` +- `e.add_list_i64(val []i64)` +- `e.add_list_f32(val []f32)` +- `e.add_list_f64(val []f64)` +- `e.add_list_string(val []string)` + +### Decoder (Deserialization) +- `e.get_bool()!` +- `e.get_u8()!` +- `e.get_u16()!` +- `e.get_u32()!` +- `e.get_u64()!` +- `e.get_i8()!` +- `e.get_i16()!` +- `e.get_i32()!` +- `e.get_i64()!` +- `e.get_f32()!` +- `e.get_f64()!` +- `e.get_string()!` +- `e.get_list_bool()!` +- `e.get_list_u8()!` +- `e.get_list_u16()!` +- `e.get_list_u32()!` +- `e.get_list_u64()!` +- `e.get_list_i8()!` +- `e.get_list_i16()!` +- `e.get_list_i32()!` +- `e.get_list_i64()!` +- `e.get_list_f32()!` +- `e.get_list_f64()!` +- `e.get_list_string()!` + +## CRUD Methods Implementation + +### Create New Instance +```v +pub fn (mut self DBCalendar) new(args CalendarArg) !Calendar { + mut o := Calendar{ + title: args.title + start_time: args.start_time + end_time: args.end_time + location: args.location + attendees: args.attendees + updated_at: ourtime.now().unix() + } + return o +} +``` + +### Save to Database +```v +pub fn (mut self DBCalendar) set(o Calendar) !u32 { + return self.db.set[Calendar](o)! +} +``` + +### Retrieve from Database +```v +pub fn (mut self DBCalendar) get(id u32) !Calendar { + mut o, data := self.db.get_data[Calendar](id)! + mut e_decoder := encoder.decoder_new(data) + self.load(mut o, mut e_decoder)! + return o +} +``` + +### Delete from Database +```v +pub fn (mut self DBCalendar) delete(id u32) ! { + self.db.delete[Calendar](id)! +} +``` + +### Check Existence +```v +pub fn (mut self DBCalendar) exist(id u32) !bool { + return self.db.exists[Calendar](id)! +} +``` + +### List All Objects +```v +pub fn (mut self DBCalendar) list() ![]Calendar { + return self.db.list[Calendar]()!.map(self.get(it)!) +} +``` + +## Example Usage Script + +Create a `.vsh` script in `examples/hero/heromodels/` to demonstrate usage: + +```v +#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run + +import freeflowuniverse.herolib.core.redisclient +import freeflowuniverse.herolib.hero.heromodels + +mut mydb := heromodels.new()! + +// Create a new object +mut o := mydb.calendar.new( + title: 'Meeting' + start_time: 1672531200 + end_time: 1672534800 + location: 'Conference Room' + attendees: ['john@example.com', 'jane@example.com'] +)! + +// Save to database +oid := mydb.calendar.set(o)! +println('Created object with ID: ${oid}') + +// Retrieve from database +mut o2 := mydb.calendar.get(oid)! +println('Retrieved object: ${o2}') + +// List all objects +mut objects := mydb.calendar.list()! +println('All objects: ${objects}') +``` + +## Best Practices + +1. Always inherit from `db.Base` struct +2. Implement all required methods (`type_name`, `dump`, `load`) +3. Use the encoder methods for consistent serialization +4. Handle errors appropriately with `!` or `or` blocks +5. Keep field ordering consistent between `dump` and `load` methods +6. Use snake_case for field names +7. Add `@[required]` attribute to mandatory fields in argument structs +8. Initialize timestamps using `ourtime.now().unix()` + +## Implementation Steps Summary + +1. Create model struct inheriting from `db.Base` +2. Implement `type_name()` method +3. Implement `dump()` method using encoder +4. Implement `load()` method using decoder +5. Create argument struct with `@[params]` attribute +6. Create database wrapper struct +7. Add model to `ModelsFactory` in `factory.v` +8. Implement CRUD methods +9. Create example usage script +10. Test the implementation with the example script +``` + +File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/core_methods.v +```v +module db + +import freeflowuniverse.herolib.data.ourtime +import freeflowuniverse.herolib.data.encoder + +pub fn (mut self DB) set[T](obj_ T) !u32 { + // Get the next ID + mut obj := obj_ + if obj.id == 0 { + obj.id = self.new_id()! + } + mut t := ourtime.now().unix() + if obj.created_at == 0 { + obj.created_at = t + } + obj.updated_at = t + + // id u32 + // name string + // description string + // created_at i64 + // updated_at i64 + // securitypolicy u32 + // tags u32 // when we set/get we always do as []string but this can then be sorted and md5ed this gies the unique id of tags + // comments []u32 + mut e := encoder.new() + e.add_u8(1) + e.add_u32(obj.id) + e.add_string(obj.name) + e.add_string(obj.description) + e.add_i64(obj.created_at) + e.add_i64(obj.updated_at) + e.add_u32(obj.securitypolicy) + e.add_u32(obj.tags) + e.add_u16(u16(obj.comments.len)) + for comment in obj.comments { + e.add_u32(comment) + } + // println('set: before dump, e.data.len: ${e.data.len}') + obj.dump(mut e)! + // println('set: after dump, e.data.len: ${e.data.len}') + self.redis.hset(self.db_name[T](), obj.id.str(), e.data.bytestr())! + return obj.id +} + +// return the data, cannot return the object as we do not know the type +pub fn (mut self DB) get_data[T](id u32) !(T, []u8) { + data := self.redis.hget(self.db_name[T](), id.str())! + + if data.len == 0 { + return error('herodb:${self.db_name[T]()} not found for ${id}') + } + + // println('get_data: data.len: ${data.len}') + mut e := encoder.decoder_new(data.bytes()) + version := e.get_u8()! + if version != 1 { + panic('wrong version in base load') + } + mut base := T{} + base.id = e.get_u32()! + base.name = e.get_string()! + base.description = e.get_string()! + base.created_at = e.get_i64()! + base.updated_at = e.get_i64()! + base.securitypolicy = e.get_u32()! + base.tags = e.get_u32()! + for _ in 0 .. e.get_u16()! { + base.comments << e.get_u32()! + } + return base, e.data +} + +pub fn (mut self DB) exists[T](id u32) !bool { + return self.redis.hexists(self.db_name[T](), id.str())! +} + +pub fn (mut self DB) delete[T](id u32) ! { + self.redis.hdel(self.db_name[T](), id.str())! +} + +pub fn (mut self DB) list[T]() ![]u32 { + ids := self.redis.hkeys(self.db_name[T]())! + return ids.map(it.u32()) +} + +// make it easy to get a base object +pub fn (mut self DB) new_from_base[T](args BaseArgs) !Base { + return T{ + Base: new_base(args)! + } +} + +fn (mut self DB) db_name[T]() string { + // get the name of the type T + mut name := T.name.to_lower_ascii().split('.').last() + // println("db_name rediskey: '${name}'") + return 'db:${name}' +} + +pub fn (mut self DB) new_id() !u32 { + return u32(self.redis.incr('db:id')!) +} + +``` + +File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/core_models.v +```v +module db + +import crypto.md5 +import freeflowuniverse.herolib.core.redisclient +import freeflowuniverse.herolib.data.ourtime + +// Group represents a collection of users with roles and permissions +@[heap] +pub struct Base { +pub mut: + id u32 + name string + description string + created_at i64 + updated_at i64 + securitypolicy u32 + tags u32 // when we set/get we always do as []string but this can then be sorted and md5ed this gies the unique id of tags + comments []u32 +} + +@[heap] +pub struct SecurityPolicy { +pub mut: + id u32 + read []u32 // links to users & groups + write []u32 // links to users & groups + delete []u32 // links to users & groups + public bool + md5 string // this sorts read, write and delete u32 + hash, then do md5 hash, this allows to go from a random read/write/delete/public config to a hash +} + +@[heap] +pub struct Tags { +pub mut: + id u32 + names []string // unique per id + md5 string // of sorted names, to make easy to find unique id, each name lowercased and made ascii +} + +``` + +File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/helpers_comments.v +```v +module db + +import crypto.md5 + + +@[params] +pub struct CommentArg { +pub mut: + comment string + parent u32 + author u32 +} + +pub fn (mut self DB) comments_get(args []CommentArg) ![]u32 { + return args.map(self.comment_get(it.comment)!) +} + +pub fn (mut self DB) comment_get(comment string) !u32 { + comment_fixed := comment.to_lower_ascii().trim_space() + return if comment_fixed.len > 0 { + hash := md5.hexhash(comment_fixed) + comment_found := self.redis.hget('db:comments', hash)! + if comment_found == '' { + id := self.new_id()! + self.redis.hset('db:comments', hash, id.str())! + self.redis.hset('db:comments', id.str(), comment_fixed)! + id + } else { + comment_found.u32() + } + } else { + 0 + } +} + +``` + +File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/db/helpers_tags.v +```v +module db + +import crypto.md5 + +pub fn (mut self DB) tags_get(tags []string) !u32 { + return if tags.len > 0 { + mut tags_fixed := tags.map(it.to_lower_ascii().trim_space()).filter(it != '') + tags_fixed.sort_ignore_case() + hash := md5.hexhash(tags_fixed.join(',')) + tags_found := self.redis.hget('db:tags', hash)! + return if tags_found == '' { + println('tags_get: new tags: ${tags_fixed.join(",")}') + id := self.new_id()! + self.redis.hset('db:tags', hash, id.str())! + self.redis.hset('db:tags', id.str(), tags_fixed.join(','))! + id + } else { + tags_found.u32() + } + } else { + 0 + } +} + +``` + +File: /Users/despiegk/code/github/incubaid/herolib/lib/hero/heromodels/calendar_event.v +```v + +module heromodels + +import freeflowuniverse.herolib.data.encoder +import freeflowuniverse.herolib.data.ourtime +import freeflowuniverse.herolib.hero.db + +// CalendarEvent represents a single event in a calendar +@[heap] +pub struct CalendarEvent { + db.Base +pub mut: + title string + start_time i64 // Unix timestamp + end_time i64 // Unix timestamp + location string + attendees []u32 // IDs of user groups + fs_items []u32 // IDs of linked files or dirs + calendar_id u32 // Associated calendar + status EventStatus + is_all_day bool + is_recurring bool + recurrence []RecurrenceRule // normally empty + reminder_mins []int // Minutes before event for reminders + color string // Hex color code + timezone string +} + +pub struct Attendee { +pub mut: + user_id u32 + status AttendanceStatus + role AttendeeRole +} + +pub enum AttendanceStatus { + no_response + accepted + declined + tentative +} + +pub enum AttendeeRole { + required + optional + organizer +} + +pub enum EventStatus { + draft + published + cancelled + completed +} + +pub struct RecurrenceRule { +pub mut: + frequency RecurrenceFreq + interval int // Every N frequencies + until i64 // End date (Unix timestamp) + count int // Number of occurrences + by_weekday []int // Days of week (0=Sunday) + by_monthday []int // Days of month +} + +pub enum RecurrenceFreq { + none + daily + weekly + monthly + yearly +} + +pub struct DBCalendarEvent { +pub mut: + db &db.DB @[skip; str: skip] +} + +pub fn (self CalendarEvent) type_name() string { + return 'calendar_event' +} + +pub fn (self CalendarEvent) dump(mut e &encoder.Encoder) ! { + e.add_string(self.title) + e.add_i64(self.start_time) + e.add_i64(self.end_time) + e.add_string(self.location) + e.add_list_u32(self.attendees) + e.add_list_u32(self.fs_items) + e.add_u32(self.calendar_id) + e.add_u8(u8(self.status)) + e.add_bool(self.is_all_day) + e.add_bool(self.is_recurring) + + // Encode recurrence array + e.add_u16(u16(self.recurrence.len)) + for rule in self.recurrence { + e.add_u8(u8(rule.frequency)) + e.add_int(rule.interval) + e.add_i64(rule.until) + e.add_int(rule.count) + e.add_list_int(rule.by_weekday) + e.add_list_int(rule.by_monthday) + } + + e.add_list_int(self.reminder_mins) + e.add_string(self.color) + e.add_string(self.timezone) +} + +fn (mut self DBCalendarEvent) load(mut o CalendarEvent, mut e &encoder.Decoder) ! { + o.title = e.get_string()! + o.start_time = e.get_i64()! + o.end_time = e.get_i64()! + o.location = e.get_string()! + o.attendees = e.get_list_u32()! + o.fs_items = e.get_list_u32()! + o.calendar_id = e.get_u32()! + o.status = unsafe { EventStatus(e.get_u8()!) } //TODO: is there no better way? + o.is_all_day = e.get_bool()! + o.is_recurring = e.get_bool()! + + // Decode recurrence array + recurrence_len := e.get_u16()! + mut recurrence := []RecurrenceRule{} + for _ in 0 .. recurrence_len { + frequency := unsafe { RecurrenceFreq(e.get_u8()!) } + interval := e.get_int()! + until := e.get_i64()! + count := e.get_int()! + by_weekday := e.get_list_int()! + by_monthday := e.get_list_int()! + + recurrence << RecurrenceRule{ + frequency: frequency + interval: interval + until: until + count: count + by_weekday: by_weekday + by_monthday: by_monthday + } + } + o.recurrence = recurrence + + o.reminder_mins = e.get_list_int()! + o.color = e.get_string()! + o.timezone = e.get_string()! +} + +@[params] +pub struct CalendarEventArg { +pub mut: + name string + description string + title string + start_time string // use ourtime module to go from string to epoch + end_time string // use ourtime module to go from string to epoch + location string + attendees []u32 // IDs of user groups + fs_items []u32 // IDs of linked files or dirs + calendar_id u32 // Associated calendar + status EventStatus + is_all_day bool + is_recurring bool + recurrence []RecurrenceRule + reminder_mins []int // Minutes before event for reminders + color string // Hex color code + timezone string + securitypolicy u32 + tags []string + comments []db.CommentArg +} + +// get new calendar event, not from the DB +pub fn (mut self DBCalendarEvent) new(args CalendarEventArg) !CalendarEvent { + mut o := CalendarEvent{ + title: args.title + location: args.location + attendees: args.attendees + fs_items: args.fs_items + calendar_id: args.calendar_id + status: args.status + is_all_day: args.is_all_day + is_recurring: args.is_recurring + recurrence: args.recurrence + reminder_mins: args.reminder_mins + color: args.color + timezone: args.timezone + } + + // Set base fields + o.name = args.name + o.description = args.description + o.securitypolicy = args.securitypolicy + o.tags = self.db.tags_get(args.tags)! + o.comments = self.db.comments_get(args.comments)! + o.updated_at = ourtime.now().unix() + + // Convert string times to Unix timestamps + mut start_time_obj := ourtime.new(args.start_time)! + o.start_time = start_time_obj.unix() + + mut end_time_obj := ourtime.new(args.end_time)! + o.end_time = end_time_obj.unix() + + return o +} + +pub fn (mut self DBCalendarEvent) set(o CalendarEvent) !u32 { + // Use db set function which now returns the ID + return self.db.set[CalendarEvent](o)! +} + +pub fn (mut self DBCalendarEvent) delete(id u32) ! { + self.db.delete[CalendarEvent](id)! +} + +pub fn (mut self DBCalendarEvent) exist(id u32) !bool { + return self.db.exists[CalendarEvent](id)! +} + +pub fn (mut self DBCalendarEvent) get(id u32) !CalendarEvent { + mut o, data := self.db.get_data[CalendarEvent](id)! + mut e_decoder := encoder.decoder_new(data) + self.load(mut o, mut e_decoder)! + return o +} + +pub fn (mut self DBCalendarEvent) list() ![]CalendarEvent { + return self.db.list[CalendarEvent]()!.map(self.get(it)!) +} + +``` + + +make the crud and example for all files in lib/hero/herofs + +think about which additional hsets we need to make it efficient + +check the implementation + +do the implementation + + + diff --git a/lib/hero/herofs/README.md b/lib/hero/herofs/README.md new file mode 100644 index 00000000..9e0d9f64 --- /dev/null +++ b/lib/hero/herofs/README.md @@ -0,0 +1,115 @@ +# HeroFS - Distributed Filesystem for HeroLib + +HeroFS is a distributed filesystem implementation built on top of HeroDB (Redis-based storage). It provides a virtual filesystem with support for files, directories, symbolic links, and binary data blobs. + +## Overview + +HeroFS implements a filesystem structure where: +- **Fs**: Represents a filesystem as a top-level container +- **FsDir**: Represents directories within a filesystem +- **FsFile**: Represents files with support for multiple directory associations +- **FsSymlink**: Represents symbolic links pointing to files or directories +- **FsBlob**: Represents binary data chunks (up to 1MB) used as file content + +## Features + +- Distributed storage using Redis +- Support for files, directories, and symbolic links +- Blob-based file content storage with integrity verification +- Multiple directory associations for files (similar to hard links) +- Filesystem quotas and usage tracking +- Metadata support for files +- Efficient lookup mechanisms using Redis hash sets + +## Installation + +HeroFS is part of HeroLib and is automatically available when using HeroLib. + +## Usage + +To use HeroFS, you need to create a filesystem factory: + +```v +import freeflowuniverse.herolib.hero.herofs + +mut fs_factory := herofs.new()! +``` + +### Creating a Filesystem + +```v +fs_id := fs_factory.fs.set(fs_factory.fs.new( + name: 'my_filesystem' + quota_bytes: 1000000000 // 1GB quota +)!)! +``` + +### Working with Directories + +```v +// Create root directory +root_dir_id := fs_factory.fs_dir.set(fs_factory.fs_dir.new( + name: 'root' + fs_id: fs_id + parent_id: 0 +)!)! + +// Create subdirectory +sub_dir_id := fs_factory.fs_dir.set(fs_factory.fs_dir.new( + name: 'documents' + fs_id: fs_id + parent_id: root_dir_id +)!)! +``` + +### Working with Blobs + +```v +// Create a blob with binary data +blob_id := fs_factory.fs_blob.set(fs_factory.fs_blob.new( + data: content_bytes + mime_type: 'text/plain' +)!)! +``` + +### Working with Files + +```v +// Create a file +file_id := fs_factory.fs_file.set(fs_factory.fs_file.new( + name: 'example.txt' + fs_id: fs_id + directories: [root_dir_id] + blobs: [blob_id] +)!)! +``` + +### Working with Symbolic Links + +```v +// Create a symbolic link to a file +symlink_id := fs_factory.fs_symlink.set(fs_factory.fs_symlink.new( + name: 'example_link.txt' + fs_id: fs_id + parent_id: root_dir_id + target_id: file_id + target_type: .file +)!)! +``` + +## API Reference + +The HeroFS module provides the following main components: + +- `FsFactory` - Main factory for accessing all filesystem components +- `DBFs` - Filesystem operations +- `DBFsDir` - Directory operations +- `DBFsFile` - File operations +- `DBFsSymlink` - Symbolic link operations +- `DBFsBlob` - Binary data blob operations + +Each component provides CRUD operations and specialized methods for filesystem management. + +## Examples + +Check the `examples/hero/herofs/` directory for detailed usage examples. diff --git a/lib/hero/herofs/specs.md b/lib/hero/herofs/specs.md new file mode 100644 index 00000000..0aad6d0c --- /dev/null +++ b/lib/hero/herofs/specs.md @@ -0,0 +1,289 @@ +# HeroFS Specifications + +This document provides detailed specifications for the HeroFS distributed filesystem implementation. + +## Architecture Overview + +HeroFS is built on top of HeroDB, which uses Redis as its storage backend. The filesystem is implemented as a collection of interconnected data structures that represent the various components of a filesystem: + +1. **Fs** - Filesystem container +2. **FsDir** - Directories +3. **FsFile** - Files +4. **FsSymlink** - Symbolic links +5. **FsBlob** - Binary data chunks + +All components inherit from the `Base` struct, which provides common fields like ID, name, description, timestamps, security policies, tags, and comments. + +## Filesystem (Fs) + +The `Fs` struct represents a filesystem as a top-level container: + +```v +@[heap] +pub struct Fs { + db.Base +pub mut: + name string + group_id u32 // Associated group for permissions + root_dir_id u32 // ID of root directory + quota_bytes u64 // Storage quota in bytes + used_bytes u64 // Current usage in bytes +} +``` + +### Key Features + +- **Name-based identification**: Filesystems can be retrieved by name using efficient Redis hash sets +- **Quota management**: Each filesystem has a storage quota and tracks current usage +- **Root directory**: Each filesystem has a root directory ID that serves as the entry point +- **Group association**: Filesystems can be associated with groups for permission management + +### Methods + +- `new()`: Create a new filesystem instance +- `set()`: Save filesystem to database +- `get()`: Retrieve filesystem by ID +- `get_by_name()`: Retrieve filesystem by name +- `delete()`: Remove filesystem from database +- `exist()`: Check if filesystem exists +- `list()`: List all filesystems +- `increase_usage()`: Increase used bytes counter +- `decrease_usage()`: Decrease used bytes counter +- `check_quota()`: Verify if additional bytes would exceed quota + +## Directory (FsDir) + +The `FsDir` struct represents a directory in a filesystem: + +```v +@[heap] +pub struct FsDir { + db.Base +pub mut: + name string + fs_id u32 // Associated filesystem + parent_id u32 // Parent directory ID (0 for root) +} +``` + +### Key Features + +- **Hierarchical structure**: Directories form a tree structure with parent-child relationships +- **Path-based identification**: Efficient lookup by filesystem ID, parent ID, and name +- **Children management**: Directories automatically track their children through Redis hash sets +- **Cross-filesystem isolation**: Directories are bound to a specific filesystem + +### Methods + +- `new()`: Create a new directory instance +- `set()`: Save directory to database and update indices +- `get()`: Retrieve directory by ID +- `delete()`: Remove directory (fails if it has children) +- `exist()`: Check if directory exists +- `list()`: List all directories +- `get_by_path()`: Retrieve directory by path components +- `list_by_filesystem()`: List directories in a filesystem +- `list_children()`: List child directories +- `has_children()`: Check if directory has children +- `rename()`: Rename directory +- `move()`: Move directory to a new parent + +## File (FsFile) + +The `FsFile` struct represents a file in a filesystem: + +```v +@[heap] +pub struct FsFile { + db.Base +pub mut: + name string + fs_id u32 // Associated filesystem + directories []u32 // Directory IDs where this file exists + blobs []u32 // IDs of file content blobs + size_bytes u64 + mime_type string // e.g., "image/png" + checksum string // e.g., SHA256 checksum of the file + accessed_at i64 + metadata map[string]string // Custom metadata +} +``` + +### Key Features + +- **Multiple directory associations**: Files can exist in multiple directories (similar to hard links in Linux) +- **Blob-based content**: File content is stored as references to FsBlob objects +- **Size tracking**: Files track their total size in bytes +- **MIME type support**: Files store their MIME type for content identification +- **Checksum verification**: Files can store checksums for integrity verification +- **Access timestamp**: Tracks when the file was last accessed +- **Custom metadata**: Files support custom key-value metadata + +### Methods + +- `new()`: Create a new file instance +- `set()`: Save file to database and update indices +- `get()`: Retrieve file by ID +- `delete()`: Remove file and update all indices +- `exist()`: Check if file exists +- `list()`: List all files +- `get_by_path()`: Retrieve file by directory and name +- `list_by_directory()`: List files in a directory +- `list_by_filesystem()`: List files in a filesystem +- `list_by_mime_type()`: List files by MIME type +- `append_blob()`: Add a new blob to the file +- `update_accessed()`: Update accessed timestamp +- `update_metadata()`: Update file metadata +- `rename()`: Rename file (affects all directories) +- `move()`: Move file to different directories + +## Symbolic Link (FsSymlink) + +The `FsSymlink` struct represents a symbolic link in a filesystem: + +```v +@[heap] +pub struct FsSymlink { + db.Base +pub mut: + name string + fs_id u32 // Associated filesystem + parent_id u32 // Parent directory ID + target_id u32 // ID of target file or directory + target_type SymlinkTargetType +} + +pub enum SymlinkTargetType { + file + directory +} +``` + +### Key Features + +- **Target type specification**: Symlinks can point to either files or directories +- **Cross-filesystem protection**: Symlinks cannot point to targets in different filesystems +- **Referrer tracking**: Targets know which symlinks point to them +- **Broken link detection**: Symlinks can be checked for validity + +### Methods + +- `new()`: Create a new symbolic link instance +- `set()`: Save symlink to database and update indices +- `get()`: Retrieve symlink by ID +- `delete()`: Remove symlink and update all indices +- `exist()`: Check if symlink exists +- `list()`: List all symlinks +- `get_by_path()`: Retrieve symlink by parent directory and name +- `list_by_parent()`: List symlinks in a parent directory +- `list_by_filesystem()`: List symlinks in a filesystem +- `list_by_target()`: List symlinks pointing to a target +- `rename()`: Rename symlink +- `move()`: Move symlink to a new parent directory +- `redirect()`: Change symlink target +- `resolve()`: Get the target ID of a symlink +- `is_broken()`: Check if symlink target exists + +## Binary Data Blob (FsBlob) + +The `FsBlob` struct represents binary data chunks: + +```v +@[heap] +pub struct FsBlob { + db.Base +pub mut: + hash string // blake192 hash of content + data []u8 // Binary data (max 1MB) + size_bytes int // Size in bytes + created_at i64 + mime_type string // MIME type + encoding string // Encoding type +} +``` + +### Key Features + +- **Content-based addressing**: Blobs are identified by their BLAKE3 hash (first 192 bits) +- **Size limit**: Blobs are limited to 1MB to ensure efficient storage and retrieval +- **Integrity verification**: Built-in hash verification for data integrity +- **MIME type and encoding**: Blobs store their content type information +- **Deduplication**: Identical content blobs are automatically deduplicated + +### Methods + +- `new()`: Create a new blob instance +- `set()`: Save blob to database (returns existing ID if content already exists) +- `get()`: Retrieve blob by ID +- `delete()`: Remove blob from database +- `exist()`: Check if blob exists +- `list()`: List all blobs +- `get_by_hash()`: Retrieve blob by content hash +- `exists_by_hash()`: Check if blob exists by content hash +- `verify_integrity()`: Verify blob data integrity against stored hash +- `calculate_hash()`: Calculate BLAKE3 hash of blob data + +## Storage Mechanisms + +HeroFS uses Redis hash sets extensively for efficient indexing and lookup: + +### Filesystem Indices +- `fs:names` - Maps filesystem names to IDs +- `fsdir:paths` - Maps directory path components to IDs +- `fsdir:fs:${fs_id}` - Lists directories in a filesystem +- `fsdir:children:${dir_id}` - Lists children of a directory +- `fsfile:paths` - Maps file paths (directory:name) to IDs +- `fsfile:dir:${dir_id}` - Lists files in a directory +- `fsfile:fs:${fs_id}` - Lists files in a filesystem +- `fsfile:mime:${mime_type}` - Lists files by MIME type +- `fssymlink:paths` - Maps symlink paths (parent:name) to IDs +- `fssymlink:parent:${parent_id}` - Lists symlinks in a parent directory +- `fssymlink:fs:${fs_id}` - Lists symlinks in a filesystem +- `fssymlink:target:${target_type}:${target_id}` - Lists symlinks pointing to a target +- `fsblob:hashes` - Maps content hashes to blob IDs + +### Data Serialization + +All HeroFS components use the HeroLib encoder for serialization: + +- Version tag (u8) is stored first +- All fields are serialized in a consistent order +- Deserialization follows the exact same order +- Type safety is maintained through V's type system + +## Special Features + +### Hard Links +Files can be associated with multiple directories through the `directories` field, allowing for hard link-like behavior. + +### Deduplication +Blobs are automatically deduplicated based on their content hash. When creating a new blob with identical content to an existing one, the existing ID is returned. + +### Quota Management +Filesystems track their storage usage and can enforce quotas to prevent overconsumption. + +### Metadata Support +Files support custom metadata as key-value pairs, allowing for flexible attribute storage. + +### Cross-Component Validation +When creating or modifying components, HeroFS validates references to other components: +- Directory parent must exist +- File directories must exist +- File blobs must exist +- Symlink parent must exist +- Symlink target must exist and match target type + +## Security Model + +HeroFS inherits the security model from HeroDB: +- Each component has a `securitypolicy` field referencing a SecurityPolicy object +- Components can have associated tags for categorization +- Components can have associated comments for documentation + +## Performance Considerations + +- All indices are stored as Redis hash sets for O(1) lookup performance +- Blob deduplication reduces storage requirements +- Multiple directory associations allow efficient file organization +- Content-based addressing enables easy integrity verification +- Factory pattern provides easy access to all filesystem components diff --git a/lib/hero/heromodels copy/instructions.md b/lib/hero/heromodels copy/instructions.md deleted file mode 100644 index 4a7dbf12..00000000 --- a/lib/hero/heromodels copy/instructions.md +++ /dev/null @@ -1,31 +0,0 @@ -distill vlang objects out of the calendr/contact/circle and create the missing parts - -organze per root object which are @[heap] and in separate file with name.v - -the rootobjects are - -- user -- group (which users are members and in which role can be admin, writer, reader, can be linked to subgroups) -- calendar (references to event, group) -- calendar_event (everything related to an event on calendar, link to one or more fs_file) -- project (grouping per project, defines swimlanes and milestones this allows us to visualize as kanban, link to group, link to one or more fs_file ) -- project_issue (and issue is specific type, e.g. task, story, bug, question,…), issue is linked to project by id, also defined priority…, on which swimlane, deadline, assignees, … ,,,, has tags, link to one or more fs_file -- chat_group (link to group, name/description/tags) -- chat_message (link to chat_group, link to parent_chat_messages and what type of link e.g. reply or reference or? , status, … link to one or more fs_file) -- fs = filesystem (link to group) -- fs_dir = directory in filesystem, link to parent, link to group -- fs_file (link to one or more fs_dir, list of references to blobs as blake192) -- fs_symlink (can be link to dir or file) -- fs_blob (the data itself, max size 1 MB, binary data, id = blake192) - -the group’s define how people can interact with the parts e.g. calendar linked to group, so readers of that group can read and have copy of the info linked to that group - -all the objects are identified by their blake192 (based on the content) - -there is a special table which has link between blake192 and their previous & next version, so we can always walk the three, both parts are indexed (this is independent of type of object) - - - - - - diff --git a/lib/hero/heromodels copy/version_history.v b/lib/hero/heromodels/beta/version_history.v similarity index 100% rename from lib/hero/heromodels copy/version_history.v rename to lib/hero/heromodels/beta/version_history.v diff --git a/lib/hero/herohandlers/handler_comment.v b/lib/hero/heromodels/openrpc/rpc_comment.v similarity index 100% rename from lib/hero/herohandlers/handler_comment.v rename to lib/hero/heromodels/openrpc/rpc_comment.v