Fictional currency management API, for integration in games, discord bots and more! shard.thevoid.cafe
currency api

🎉 Initialize Shard project structure

+1
.env.example
···
··· 1 + PORT: 80
+9
.gitignore
···
··· 1 + ### Environment variables 2 + .env 3 + 4 + ### IDE files 5 + .idea/ 6 + .vscode/ 7 + 8 + ### Dependencies 9 + node_modules/
+21
LICENSE
···
··· 1 + MIT License 2 + 3 + Copyright (c) 2025 Johannes Reckers. 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
+218
README.md
···
··· 1 + # Shard Currency API 2 + 3 + A **very small**, fictional currency API for games and community systems. 4 + 5 + * One balance per user 6 + * No real-world money 7 + * Users can **never** change their own balance 8 + * Balance changes are performed only by **actors** via scoped tokens 9 + 10 + --- 11 + 12 + ## Core Concepts 13 + 14 + ### User 15 + 16 + A human account with exactly one balance. 17 + 18 + ### Actor 19 + 20 + A trusted external service (Minecraft server, Discord bot, admin tool, etc.) that may create transactions. 21 + 22 + ### Transaction 23 + 24 + An immutable record of a balance change. 25 + 26 + ### Link Code 27 + 28 + A short-lived, one-time code used to create an actor and issue its token. 29 + 30 + --- 31 + 32 + ## Authentication 33 + 34 + ### User Auth 35 + 36 + ```http 37 + POST /v1/auth/register 38 + POST /v1/auth/login 39 + ``` 40 + 41 + User tokens: 42 + 43 + * allow viewing balance 44 + * allow viewing transactions 45 + * allow requesting link codes 46 + * **never allow transactions** 47 + 48 + --- 49 + 50 + ## User 51 + 52 + ```json 53 + { 54 + "id": "usr_01HXYZ", 55 + "email": "user@example.com", 56 + "balance": 420, 57 + "created_at": "2025-09-01T12:00:00Z" 58 + } 59 + ``` 60 + 61 + --- 62 + 63 + ## Actor 64 + 65 + Actors represent *authority*, not users. 66 + 67 + ```json 68 + { 69 + "id": "act_01HABC", 70 + "name": "Survival SMP", 71 + "permissions": { 72 + "max_single_transaction": 100, 73 + "daily_cap": 5000, 74 + "allow_negative": true 75 + }, 76 + "expires_at": "2025-10-10T00:00:00Z" 77 + } 78 + ``` 79 + 80 + Notes: 81 + 82 + * Permissions are defined by the API / API_KEY holder 83 + * Public clients cannot choose permissions 84 + 85 + --- 86 + 87 + ## Actor Token 88 + 89 + Issued when a link code is consumed. 90 + 91 + ```json 92 + { 93 + "actor_id": "act_01HABC", 94 + "token": "act_live_abc123", 95 + "expires_at": "2025-10-10T00:00:00Z" 96 + } 97 + ``` 98 + 99 + Used as: 100 + 101 + ```http 102 + Authorization: Bearer act_live_abc123 103 + ``` 104 + 105 + --- 106 + 107 + ## Link Codes 108 + 109 + ### Request a Link Code 110 + 111 + Used by a trusted UI (e.g. Discord bot using `API_KEY`). 112 + 113 + ```http 114 + POST /v1/links/request 115 + Authorization: ApiKey ${API_KEY} 116 + ``` 117 + 118 + ```json 119 + { 120 + "expires_in": 300 121 + } 122 + ``` 123 + 124 + Response: 125 + 126 + ```json 127 + { 128 + "code": "Z9FQ-2KDL" 129 + } 130 + ``` 131 + 132 + --- 133 + 134 + ### Consume a Link Code 135 + 136 + Used by the external service (e.g. Minecraft server). 137 + 138 + ```http 139 + POST /v1/links/consume 140 + ``` 141 + 142 + ```json 143 + { 144 + "code": "Z9FQ-2KDL", 145 + "name": "Survival SMP" 146 + } 147 + ``` 148 + 149 + Response: 150 + 151 + ```json 152 + { 153 + "actor_id": "act_01HABC", 154 + "token": "act_live_abc123", 155 + "expires_at": "2025-10-10T00:00:00Z" 156 + } 157 + ``` 158 + 159 + --- 160 + 161 + ## Transactions 162 + 163 + Only actors or the system may create transactions. 164 + 165 + ```http 166 + POST /v1/transactions 167 + Authorization: Bearer ACTOR_TOKEN 168 + ``` 169 + 170 + ```json 171 + { 172 + "user_id": "usr_01HXYZ", 173 + "amount": 25, 174 + "reason": "Quest reward" 175 + } 176 + ``` 177 + 178 + Rules: 179 + 180 + * `amount > 0` → credit 181 + * `amount < 0` → debit 182 + * Amount must be within actor limits 183 + * Transactions are append-only 184 + 185 + --- 186 + 187 + ## Transaction Record 188 + 189 + ```json 190 + { 191 + "id": "txn_01HZAB", 192 + "user_id": "usr_01HXYZ", 193 + "amount": 25, 194 + "reason": "Quest reward", 195 + "actor_id": "act_01HABC", 196 + "created_at": "2025-09-10T18:42:00Z" 197 + } 198 + ``` 199 + 200 + --- 201 + 202 + ## API_KEY (Host-Level Access) 203 + 204 + ```http 205 + Authorization: ApiKey ${API_KEY} 206 + ``` 207 + 208 + Capabilities: 209 + 210 + * Create link codes 211 + * Create actors directly 212 + * Create transactions without actor limits 213 + 214 + All actions are still logged. 215 + 216 + ⚠️ Never expose this key publicly. 217 + 218 + ---
+26
bun.lock
···
··· 1 + { 2 + "lockfileVersion": 1, 3 + "configVersion": 1, 4 + "workspaces": { 5 + "": { 6 + "name": "shard", 7 + "dependencies": { 8 + "hono": "^4.11.1", 9 + }, 10 + "devDependencies": { 11 + "@types/bun": "^1.3.5", 12 + }, 13 + }, 14 + }, 15 + "packages": { 16 + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], 17 + 18 + "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], 19 + 20 + "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], 21 + 22 + "hono": ["hono@4.11.1", "", {}, "sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg=="], 23 + 24 + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 25 + } 26 + }
+27
package.json
···
··· 1 + { 2 + "name": "shard", 3 + "version": "0.1.0", 4 + "description": "Fictional currency management API, for integration in games, discord bots and more!", 5 + "main": "src/index.ts", 6 + "scripts": { 7 + "start": "bun .", 8 + "dev": "bun --watch ." 9 + }, 10 + "keywords": [ 11 + "shard", 12 + "currency", 13 + "management", 14 + "api", 15 + "integration", 16 + "games", 17 + "discord" 18 + ], 19 + "author": "Johannes Reckers", 20 + "license": "MIT", 21 + "dependencies": { 22 + "hono": "^4.11.1" 23 + }, 24 + "devDependencies": { 25 + "@types/bun": "^1.3.5" 26 + } 27 + }
+25
src/index.ts
···
··· 1 + // Import Dependencies 2 + import { Hono } from "hono"; 3 + import { version } from "../package.json"; 4 + 5 + // Import Routers 6 + import { v1 } from "./routes/v1"; 7 + 8 + // Instantiate new Hono instance 9 + const app = new Hono(); 10 + 11 + // Server information notice 12 + app.get("/", (c) => 13 + c.text( 14 + `This domain is running a Shard currency server instance, on version ${version}\n\nFor more information, visit https://tangled.org/thevoid.cafe/shard`, 15 + ), 16 + ); 17 + 18 + // Router mappings 19 + app.route("/v1", v1); 20 + 21 + // Export Hono instance configuration 22 + export default { 23 + fetch: app.fetch, 24 + port: Bun.env.PORT || 80, 25 + };
+5
src/routes/v1/auth/index.ts
···
··· 1 + // Import Dependencies 2 + import { Hono } from "hono"; 3 + 4 + // Instantiate new Hono instance 5 + export const auth = new Hono();
+11
src/routes/v1/index.ts
···
··· 1 + // Import Dependencies 2 + import { Hono } from "hono"; 3 + 4 + // Import Routers 5 + import { auth } from "./auth"; 6 + 7 + // Instantiate new Hono instance 8 + export const v1 = new Hono(); 9 + 10 + // Router mappings 11 + v1.route("/auth", auth);
+5
src/routes/v1/links/index.ts
···
··· 1 + // Import Dependencies 2 + import { Hono } from "hono"; 3 + 4 + // Instantiate new Hono instance 5 + export const links = new Hono();
+5
src/routes/v1/transactions/index.ts
···
··· 1 + // Import Dependencies 2 + import { Hono } from "hono"; 3 + 4 + // Instantiate new Hono instance 5 + export const transactions = new Hono();