Gleam SDK for Pocketenv
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Switch crypto dependency from enacl to kcl

Replace libsodium enacl NIF with the pure-Elixir kcl implementation.
Add pocketenv_crypto_ffi.erl implementing box_seal/2 (ephemeral X25519
keypair, BLAKE2b-derived 24-byte nonce, encrypt via Kcl). Update
gleam.toml, manifest.toml and CHANGELOG.md.

+27 -6
+2 -2
CHANGELOG.md
··· 9 9 10 10 ### Added 11 11 12 - - `pocketenv/crypto` module with `seal/1` (NaCl sealed-box encryption via libsodium) and `redact/1` (display-safe masking) 12 + - `pocketenv/crypto` module with `seal/1` (NaCl sealed-box encryption via `kcl`) and `redact/1` (display-safe masking) 13 13 - `pocketenv/sshkeys` module with `get/1` and `put/3` — private keys are encrypted before transmission and a redacted display value is stored alongside 14 14 - `secrets.put/3` now encrypts the secret value client-side before sending 15 15 - `network.setup_tailscale/2` now encrypts the auth key and stores a redacted display value 16 16 - `network.get_tailscale_auth_key/1` now returns the redacted display value 17 - - `enacl` dependency for Erlang libsodium NIF bindings 17 + - `kcl` dependency for pure-Elixir NaCl crypto_box implementation (no libsodium required) 18 18 19 19 ## [1.0.0] - 2026-04-01 20 20
+1 -1
gleam.toml
··· 22 22 gleam_http = ">= 4.0.0 and < 5.0.0" 23 23 gleam_httpc = ">= 5.0.0 and < 6.0.0" 24 24 gleam_json = ">= 3.0.0 and < 4.0.0" 25 - enacl = ">= 1.2.1 and < 2.0.0" 25 + kcl = ">= 0.1.0 and < 1.0.0" 26 26 27 27 [dev-dependencies] 28 28 gleeunit = ">= 1.0.0 and < 2.0.0"
+8 -2
manifest.toml
··· 2 2 # You typically do not need to edit this file 3 3 4 4 packages = [ 5 - { name = "enacl", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "enacl", source = "hex", outer_checksum = "67BBBEDDD2564DC899A3DCBC3765CD6AD71629134F1E500A50EC071F0F75E552" }, 5 + { name = "chacha20", version = "0.3.6", build_tools = ["mix"], requirements = [], otp_app = "chacha20", source = "hex", outer_checksum = "40BC6B1F4816661C07A3244D46D74640F108F69EB61F96D2DD22DCBA0E7FCA38" }, 6 + { name = "curve25519", version = "0.1.4", build_tools = ["mix"], requirements = [], otp_app = "curve25519", source = "hex", outer_checksum = "3460590592DA61D5D0C309E2EC469290963129BFB6EE6E5F692AE8E0334161B3" }, 7 + { name = "ed25519", version = "0.2.5", build_tools = ["mix"], requirements = [], otp_app = "ed25519", source = "hex", outer_checksum = "87233BFC85D0BE366EDDF870B6C021396FA34BDC48472AA8582B7333D1459147" }, 8 + { name = "equivalex", version = "0.1.4", build_tools = ["mix"], requirements = [], otp_app = "equivalex", source = "hex", outer_checksum = "E63AF7625D18D1BE6CB88AAEEF5046BE6C0B3D7AA8E735A51203ED17076BE8BA" }, 6 9 { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, 7 10 { name = "gleam_http", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "82EA6A717C842456188C190AFB372665EA56CE13D8559BF3B1DD9E40F619EE0C" }, 8 11 { name = "gleam_httpc", version = "5.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "C545172618D07811494E97AAA4A0FB34DA6F6D0061FDC8041C2F8E3BE2B2E48F" }, 9 12 { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, 10 13 { name = "gleam_stdlib", version = "0.70.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86949BF5D1F0E4AC0AB5B06F235D8A5CC11A2DFC33BF22F752156ED61CA7D0FF" }, 11 14 { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, 15 + { name = "kcl", version = "0.6.6", build_tools = ["mix"], requirements = ["curve25519", "ed25519", "poly1305", "salsa20"], otp_app = "kcl", source = "hex", outer_checksum = "4470B99394DC32EA015E586E66A031E4DF1AB8AD8329B49B8CA79C41711C3C16" }, 16 + { name = "poly1305", version = "0.4.5", build_tools = ["mix"], requirements = ["chacha20", "equivalex"], otp_app = "poly1305", source = "hex", outer_checksum = "2A24B02A57D56C2B459F1D6265391843A6F3591137DB7400D32B7EA26B9E3EF1" }, 17 + { name = "salsa20", version = "0.3.4", build_tools = ["mix"], requirements = [], otp_app = "salsa20", source = "hex", outer_checksum = "B6BD54042E4FC419D9B7956D2D1C0A730DC3C549D847842C0AC3553BE8EBEDF0" }, 12 18 ] 13 19 14 20 [requirements] 15 - enacl = { version = ">= 1.2.1 and < 2.0.0" } 16 21 gleam_http = { version = ">= 4.0.0 and < 5.0.0" } 17 22 gleam_httpc = { version = ">= 5.0.0 and < 6.0.0" } 18 23 gleam_json = { version = ">= 3.0.0 and < 4.0.0" } 19 24 gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 20 25 gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 26 + kcl = { version = ">= 0.1.0 and < 1.0.0" }
+1 -1
src/pocketenv/crypto.gleam
··· 38 38 39 39 // --- FFI --- 40 40 41 - @external(erlang, "enacl", "box_seal") 41 + @external(erlang, "pocketenv_crypto_ffi", "box_seal") 42 42 fn box_seal(message: BitArray, public_key: BitArray) -> BitArray 43 43 44 44 @external(erlang, "binary", "decode_hex")
+15
src/pocketenv_crypto_ffi.erl
··· 1 + -module(pocketenv_crypto_ffi). 2 + -export([box_seal/2]). 3 + 4 + %% Implements libsodium crypto_box_seal (anonymous sealed box) without libsodium: 5 + %% 6 + %% 1. Generate ephemeral X25519 keypair via :crypto 7 + %% 2. Derive nonce = first 24 bytes of BLAKE2b(eph_pk || recipient_pk) 8 + %% 3. Encrypt with NaCl crypto_box via Kcl 9 + %% 4. Output = eph_pk (32 bytes) || ciphertext 10 + box_seal(Message, RecipientPK) -> 11 + {EphPK, EphSK} = crypto:generate_key(ecdh, x25519), 12 + FullHash = crypto:hash(blake2b, <<EphPK/binary, RecipientPK/binary>>), 13 + <<Nonce:24/binary, _/binary>> = FullHash, 14 + {Ciphertext, _State} = 'Elixir.Kcl':box(Message, Nonce, EphSK, RecipientPK), 15 + <<EphPK/binary, Ciphertext/binary>>.