KV database

Each physical tc-kv owns independent string/hash storage. Computers authorized on either of its NETWORK colors share that one database. Obtain local db = peripheral.find("kv_database", "red"), or wrap an exact name/color. Use dot calls. Keys, fields and values are byte strings, including empty and NUL; no number/boolean/table coercion. Missing scalar results are one nil, not zero returns, false or empty string.

Complete commands

Signature Returns Behavior
db.get(key) string or nil Missing/expired nil; live hash raises E_WRONGTYPE
db.set(key, value) "OK" Replace either type; clear old deadline
db.del(key, ...) integer At least one key; count each existing distinct key once
db.exists(key, ...) integer At least one key; repeated live arguments count repeatedly
db.type(key) "none", "string", "hash" Expired is none
db.hget(key, field) string or nil Missing key/field nil
db.hset(key, field, value, ...) integer At least one complete pair; last repeated field wins; count newly added distinct fields once
db.hdel(key, field, ...) integer At least one field; count each removed field once; final field deletes key
db.hexists(key, field) 0 or 1 Integer, not boolean
db.hgetall(key) string-keyed map Missing gives independent {}; not an ordered array
db.hlen(key) integer Missing gives 0
db.expire(key, seconds) 0 or 1 Integer simulation seconds; absent 0, accepted live expiry/deletion 1
db.ttl(key) integer -2 absent; -1 persistent; otherwise rounded remaining seconds
db.persist(key) 0 or 1 1 only when removing a live existing deadline
db.getLimits() copied map Static reachable capacities, including while unpowered

All hash commands reject a live string with E_WRONGTYPE (message contains WRONGTYPE). HSET/HDEL preserve an existing hash deadline unless HDEL removes the last field. Odd/missing HSET operands fail before publication. No condition flags, increment, lists/sets, scan, pub/sub, external Redis, transactions or field expiry.

Expiry

Positive expire stores current_tick + seconds*60; safe integer duration/arithmetic validation happens even if the key is absent. Nonpositive duration deletes a live key immediately. All commands treat a key absent at or beyond its deadline even if incremental physical cleanup has not reached it.

For a live key TTL is nearest-second floor((deadline-tick+30)/60), half seconds up. Zero can still mean a live key, unlike -2. SET removes expiry, hash mutation preserves it, EXPIRE replaces it, PERSIST removes it. A new hash after expiry has no old fields or deadline. Pause/server downtime does not advance ticks; unpowered or carried tagged items do consume ongoing simulation time. Cold load/replacement does not renew TTL.

Atomicity, errors and capacities

Each command validates all operands/types, work and final capacity before mutation. A failed multi-field HSET/HDEL/DEL cannot publish a prefix or alter the deadline. Earlier successful commands remain if later work fails. Readback is copied; E_BUSY/E_LIMIT is not a successful empty map. Ordinary type/capacity/domain errors are catchable; fatal shared callback/allocation exhaustion faults the run.

Device limit key Initial value
keys 128
bytes (keys + string values + hash fields/values) 16384
key_bytes (also each field) 256
value_bytes 8192
hash_fields 128 per hash
hash_bytes 8192 per hash
total_hash_fields 256
command_arguments 128
snapshot_bytes 65536 separately reserved accounting units
reclaim_keys_per_device 8
reclaim_devices_per_tick 2

Common call bytes 65,536 / entries 4,096 and callback allocations/work still apply. Metadata reservation is separate from content so a full accepted database can be saved/mined. Reads count logical expiry immediately; bounded background reclamation visits unpowered databases too and never resurrects expired values. Limits are alpha safety ceilings, not real-time guarantees.

Catch a wrong-type read safely

This complete program logs a string key or a catchable WRONGTYPE diagnostic without assuming every error is a table. It leaves any existing key untouched.

return {
  api_version = 1,
  handlers = {
    start = function(event)
      local db = peripheral.find("kv_database", "red")
      if not db then
        computer.log("Connect a database on red NETWORK")
        return
      end
      local ok, value = pcall(db.get, "counter")
      if ok then
        computer.log(value or "No counter yet")
      elseif type(value) == "table" then
        computer.log(value.code .. ": " .. value.message)
      else
        computer.log(tostring(value))
      end
    end
  }
}

Mining and shared storage

Actual player/robot material mining carries a validated tc_kv tag in one item. Replacement imports it under a new identity with old absolute deadlines. Blueprints, ghost-only tags, clones and untagged items start empty. Destruction destroys data. Malformed/unknown item data is refused/refunded, not normalized into empty storage; unsupported retained data stays inert and unmineable for recovery.

Run kv-program with one red NETWORK database to see a 30-second hash and delayed TTL read. Run shared-telemetry with the combined setup for two-computer sharing and a separate private database.

If a value appears missing, check which physical name/color you wrapped, type and TTL. If WRONGTYPE occurs, use type/hgetall or deliberately replace with SET; do not treat a hash as a serialized string. Missing after transport may be legitimate expiry. No power means active calls fail, not empty results. See troubleshooting.