t1yOS
Expert knowledge for the t1yOS Serverless platform. Use this skill whenever the user mentions t1yOS, cloud functions, .jsc files, serverless development, or needs help with t1yOS platform features (cl
SKILL.mdSKILL.md
GitHubt1yOS Platform Skill
Expert knowledge for developing on the t1yOS Serverless cloud OS.
When to consult reference files
This SKILL.md covers the essential rules, patterns, and mental model. Detailed
API tables and exhaustive option lists live in references/. Read them when you
need exact signatures:
| Reference | When to read |
|---|---|
references/cloud-function.md |
Need exact function signatures for context, http, os, jwt modules |
references/database.md |
Database CRUD with exact parameter types, query operators, SQL ops |
references/crypto.md |
Hashing, AES-GCM, password hashing, encoding function names and signatures |
references/mcp.md |
MCP Server configuration and tool catalog with parameters |
references/sdks.md |
Client SDK integration (JS, Android, Swift, Flutter, C#, Go) |
references/security.md |
HMAC-SHA256 signing spec, AES-256-GCM safe mode, security best practices |
references/constants.md |
HTTP status codes, methods, MIME type constants |
Platform mental model
t1yOS is a Serverless cloud OS. It bundles cloud functions, a NoSQL database (with SQL-to-BSON), file storage, environment variables, scheduled tasks, and an MCP Server into one platform.
The runtime is Go/fasthttp with an Express.js-like JavaScript API. Files use
.jsc extension under /.functions/ directory.
Cloud function URL format — CRITICAL
Cloud function URLs MUST include the Application ID in the path. The format is:
https://myapp.t1y.net/<AppID>/<function-name>.jsc
NOT https://myapp.t1y.net/<function-name>.jsc (missing AppID → 404).
For example, if your AppID is 1001 and your function is index.jsc:
https://myapp.t1y.net/1001/index.jsc?name=Zhangsan
How to get the AppID
If the user's MCP Server is configured, call the app_info MCP tool to
retrieve the Application ID:
Use the t1yOS MCP tool: app_info
→ Returns: { appId: "1001", name: "...", ... }
If MCP is NOT configured, ask the user for their AppID. Tell them to find it in the t1yOS Console under their application settings.
When testing or sharing URLs
Whenever you generate a cloud function URL for testing (curl, browser, etc.), always:
- First try to get the AppID via the
app_infoMCP tool - If unavailable, ask the user: "请提供你的 t1yOS 应用 ID(可在控制台中找到)"
- Construct the URL as
https://myapp.t1y.net/<AppID>/<function>.jsc
Required workflow: Write → Deploy → Test (MANDATORY)
After writing a cloud function, you MUST deploy and test it. Do NOT just write the code and stop — the job is not done until the function has been verified working with a real request.
Step 1: Deploy the function
Always use MCP tools — the preferred and most efficient method:
- New function:
function_createwithnameandcode— auto-compiles, no manual rebuild - Update existing:
function_updatewithnameand updatedcode— auto-compiles
MCP tools handle compilation automatically. Do NOT write .jsc files via file
tools and then manually call function_rebuild_all — that wastes tokens.
⚠️ All cloud functions MUST be placed in the /.functions/ directory (with
dot prefix). /functions/ (without dot) is WRONG and will not work.
If MCP is not configured, guide the user to deploy via WebIDE or the platform console.
Step 2: Test the function (REQUIRED — do NOT skip)
Make a real HTTP request to the deployed cloud function. Use whatever HTTP tool is available in the user's environment:
- macOS / Linux:
curlorhttpie - Windows:
curl(CMD/PowerShell) orInvoke-WebRequest(PowerShell) - Any platform:
python -c "import urllib.request; ..."orfetch(Node.js)
Before testing, you MUST construct the correct URL:
- Get the AppID via MCP
app_infotool (or ask the user) - Build the URL:
https://myapp.t1y.net/<AppID>/<function>.jsc - Add query parameters or request body as appropriate for the endpoint
Example curl commands:
# GET request with query params
curl -s "https://myapp.t1y.net/<AppID>/index.jsc?name=TestUser"
# POST request with JSON body
curl -s -X POST "https://myapp.t1y.net/<AppID>/index.jsc" \
-H "Content-Type: application/json" \
-d '{"name": "TestUser"}'
# POST request with form data
curl -s -X POST "https://myapp.t1y.net/<AppID>/login.jsc" \
-d "username=admin&password=123456"
Step 3: Debug if the test fails
If the test request returns an error or unexpected result:
- Check logs: Use MCP
function_get_logwith the function nameMCP tool: function_get_log Parameters: { name: "index", lines: 50 } - Redeploy if needed: Use
function_updateafter fixing the code - Common checks: verify the URL includes AppID, verify the function is in
/.functions/directory, verify.jscextension, check HTTP method matches
Step 4: Report the result
After testing, report to the user:
- ✅ What was tested and whether it passed
- 📋 The actual response/result
- 🐛 If failed: the error details and suggested fix
If the test fails, fix the code and test again. Do not leave the user with untested code.
Critical rules — read these first
.jscextension required: Cloud functions MUST use.jsc(NOT.js).onRequest()takes NO parameters: The entry function isfunction onRequest() { ... }— no arguments.ctxcomes fromrequire('context'): Always writeconst ctx = require('context')at the top to access request/response methods.- Everything on ctx is a FUNCTION:
ctx.method(),ctx.query('key'),ctx.body(),ctx.get('Header-Name'),ctx.set('Header', 'value')— all function calls, NOT property access. - No ES modules: Use
require()notimport. Nomodule.exportsfor the entry function (but custom helper modules CAN use it). - No npm: Only built-in modules available:
context,http,mongo,crypto,os,jwt. You CANrequire()local/remote.jsand.jsonfiles. - Go templates:
ctx.render()uses Go{{ .Field }}syntax. - Logging: Use
console.log(),console.info(),console.debug(),console.warn(),console.error(). NOTctx.log(). - Hidden files blocked: All dot-prefixed files and directories are inaccessible via HTTP, except
.jscfiles within the/.functions/directory. - MUST test after writing: After deploying a cloud function, you are
REQUIRED to test it with a real HTTP request (via MCP
function_runor curl). Never just write code and stop — verify it works.
Core cloud function structure
// Always require context at the top
const ctx = require('context')
const http = require('http')
// onRequest takes NO parameters — all state comes from ctx
function onRequest() {
// ctx.method() returns the HTTP method string
if (ctx.method() === 'GET') {
// ctx.query('key') gets a single query parameter
const name = ctx.query('name', 'World') // Second arg = default
return `Hello, ${name}!`
}
if (ctx.method() === 'POST') {
// ctx.body() returns the parsed request body
const body = ctx.body()
return { received: body }
}
// Return any value — auto-serialized to response
return { status: 'ok' }
}
The onRequest return pattern
onRequest() returns a value OR calls a ctx response method and returns null:
| Pattern | Code |
|---|---|
| Return data (auto-serialize) | return { key: 'value' } |
| Return string | return 'Hello' |
| Render template | ctx.render('/.templates/page.tmpl', data); return null |
| Send file | ctx.sendFile('/path/to/file.png'); return null |
| Trigger download | ctx.download('/path/to/file.pdf'); return null |
| Redirect | ctx.redirect(302, 'https://example.com'); return null |
| XML response | ctx.XML(data); return null |
| JSONP response | ctx.jsonp(data); return null |
| Custom status only | ctx.sendStatus(404); return null |
Rule: when using ctx.render(), ctx.sendFile(), ctx.download(),
ctx.redirect(), ctx.XML(), ctx.jsonp(), or ctx.sendStatus(), always
return null after — you've already sent the response.
Context (ctx) API quick reference
Every method on ctx is a function call. Here are the most common ones:
Reading the request
const ctx = require('context')
function onRequest() {
ctx.method() // → "GET", "POST", etc.
ctx.query('name') // → query param value (string)
ctx.query('name', 'default') // → with default value
ctx.queries() // → ALL query params as object
ctx.body() // → parsed request body (JSON→object, else string)
ctx.hasBody() // → true/false: is there a body?
ctx.isJSON() // → true/false: is Content-Type application/json?
ctx.isForm() // → true/false: is it form data?
ctx.formValue('field') // → form field value
ctx.formValue('field', 'default') // → with default
ctx.get('Header-Name') // → single request header value
ctx.get('X-Forwarded-For', '0.0.0.0') // → with default
ctx.getReqHeaders() // → ALL request headers as object
ctx.hasHeader('Header-Name') // → true/false: header exists?
ctx.ip() // → client IP string
ctx.ips() // → X-Forwarded-For IP array
ctx.userAgent() // → User-Agent string
ctx.cookies('name') // → get cookie value
ctx.cookies('name', 'default') // → with default
}
Writing the response
function onRequest() {
ctx.set('Content-Type', 'text/plain') // set response header
ctx.sendStatus(200) // set HTTP status code
ctx.sendStatus(http.StatusNotFound) // use constants from require('http')
ctx.cookie({
// set a cookie
name: 'session',
value: 'abc123',
max_age: 86400,
http_only: true,
secure: true,
same_site: 'Strict',
})
ctx.ClearCookie('session') // clear specific cookie (note: capital C)
ctx.ClearCookie() // clear ALL cookies
ctx.redirect(302, 'https://example.com') // redirect (statusCode FIRST)
ctx.render('/.templates/page.tmpl', {
// render Go template
Title: 'My Page',
User: { Name: 'WangHua', Age: 23 },
})
ctx.sendFile('/path/to/file.png') // send file content
ctx.download('/path/to/file.pdf') // trigger browser download
ctx.XML({ name: 'WangHua', age: 23 }) // send XML response
ctx.jsonp({ name: 'WangHua' }) // send JSONP, auto callback
ctx.jsonp({ name: 'WangHua' }, 'cb') // custom callback name
}
File upload
function onRequest() {
const fileInfo = ctx.formFileInfo('file') // get upload file info
// fileInfo: { filename, size, header }
ctx.saveFile('file', '/.private/uploads/' + fileInfo.filename) // save file
return 'uploaded'
}
Request-level storage
function onRequest() {
ctx.locals('uid', 123456) // store
ctx.locals('name', 'WangHua') // store
const uid = ctx.locals('uid') // retrieve → 123456
}
Security methods
function onRequest() {
// Verify HMAC-SHA256 signature (returns true/false, does NOT throw)
if (!ctx.sign()) {
return '签名验证失败'
}
// Enable AES-256-GCM safe mode (encrypts response, decrypts request)
ctx.enableSafeMode()
}
Database usage patterns
const db = require('mongo')
function onRequest() {
const users = db.collection('users')
// ── Create ──────────────────────────────────────────────
// insertOne returns the ObjectID (single ID string), NOT the full document
const objectId = users.insertOne({
name: 'WangHua',
age: 23,
email: 'test@example.com',
})
// → "60d5f7c8a1b2c3d4e5f6a7b8"
// insertMany returns array of ObjectIDs
const ids = users.insertMany([
{ name: 'ZhangSan', age: 25 },
{ name: 'LiSi', age: 30 },
])
// → ["60d5...b8", "60d5...b9"]
// ── Read ────────────────────────────────────────────────
// ⚠️ findOne THROWS an exception when no document matches — NOT returns null!
// Always wrap findOne in try-catch to handle the 'no documents in result' error:
let user
try {
user = users.findOne({ name: 'WangHua' })
} catch (e) {
// No matching document — handle gracefully
user = null
}
if (!user) {
return { code: 404, message: '用户不存在' }
}
// find(page, size, filter, sort) — page starts at 1
const result = users.find(1, 10, {}, { createdAt: -1 })
// → { results: [...], page: 1, size: 10, pagination: { totalItems, totalPages } }
// find with filter
const adults = users.find(1, 20, { age: { $gt: 18 } }, { name: 1 })
// ── Update ──────────────────────────────────────────────
// updateOne(filter, data) — data is the replacement fields directly
// Returns modified count (Number)
const modified = users.updateOne(
{ name: 'WangHua' }, // filter
{ age: 24, city: '深圳' } // new field values (NO $set wrapper!)
)
// updateMany: same pattern, affects all matching docs
const count = users.updateMany({ role: 'viewer' }, { role: 'member' })
// ── Delete ──────────────────────────────────────────────
const deleted1 = users.deleteOne({ name: 'WangWu' }) // returns count
const deleted2 = users.deleteMany({ status: 'inactive' }) // returns count
// ── Count & Distinct ────────────────────────────────────
const total = users.count({}) // filter REQUIRED
const active = users.count({ status: 'active' })
const cities = users.distinct('city', {}) // (field, filter)
const adminCities = users.distinct('city', { role: 'admin' })
// ── Aggregate ───────────────────────────────────────────
const pipeline = [{ $group: { _id: '$city', count: { $sum: 1 } } }, { $sort: { count: -1 } }]
const aggResult = orders.aggregate(pipeline)
// ── Collection management ───────────────────────────────
db.collection('logs').create() // explicitly create collection
db.getCollections() // list all collection names → String[]
db.collection('logs').clear() // clear all docs, keep structure → count
db.collection('temp').drop() // DESTROY collection entirely
// ── Utility ─────────────────────────────────────────────
const oid = db.toObjectID('60d5f7c8a1b2c3d4e5f6a7b8')
let userByOid
try {
userByOid = users.findOne({ _id: oid })
} catch (e) {
userByOid = null
}
}
SQL operations (on db directly — NOT on collection)
const db = require('mongo')
function onRequest() {
// These are called on `db`, not on a collection reference
db.insertBySQL("INSERT INTO users (name, age) VALUES ('ZhaoLiu', 27)")
db.selectBySQL('SELECT name, age FROM users WHERE age > 18 ORDER BY age DESC')
db.updateBySQL("UPDATE users SET status = 'active' WHERE age >= 18")
db.deleteBySQL("DELETE FROM users WHERE status = 'inactive'")
}
Key database rules
- insertOne returns ObjectID (string), NOT the full document
- insertMany returns ObjectID[] (array of strings)
- ⚠️ findOne THROWS on no match: When no document matches the filter,
findOnethrows an exception (mongo: no documents in result). Always wrap it in try-catch instead of checking for null.find()also throws when the result set is empty. - find signature:
find(page, size, filter, sort)— 4 positional args. Page starts at 1 (not 0). - updateOne/updateMany: second arg is direct field values. No
$setwrapper needed. - count(filter): filter is REQUIRED. Use
{}for all documents. - distinct(field, filter): two args, both required. Use
{}for no filter. - System auto-creates
createdAtandupdatedAtfields on insert/update. - Collections auto-create on first insert.
JWT authentication
The t1yOS JWT API is completely different from standard jsonwebtoken:
const jwt = require('jwt')
const ctx = require('context')
const http = require('http')
function onRequest() {
// ── Generate token ──────────────────────────────────────
// jwt.generateToken(userId, roles, expiresAtMinutes)
const MINUTES_7_DAYS = 7 * 24 * 60
const token = jwt.generateToken('uid123', ['user', 'super_admin'], MINUTES_7_DAYS)
// ── Verify token ────────────────────────────────────────
// jwt.verifyToken() — NO arguments! Auto-reads from request context
// Returns { userId, roles } or throws on invalid/missing token
const payload = jwt.verifyToken()
if (payload.userId !== 'uid123') {
ctx.sendStatus(http.StatusUnauthorized)
return { code: http.StatusUnauthorized, message: '权限不足', data: null }
}
if (!payload.roles.includes('super_admin')) {
ctx.sendStatus(http.StatusUnauthorized)
return { code: http.StatusUnauthorized, message: '权限不足', data: null }
}
return `Hello, ${payload.userId}!`
}
Key JWT rules:
generateToken(userId, roles, expiresAtMinutes)— 3 args, expiry in MINUTESverifyToken()— NO arguments, auto-verifies from request Authorization header- The
rolesparameter is an array of strings:['user', 'admin']
Crypto module
const crypto = require('crypto')
function onRequest() {
// ── Hashing (all take one string, return hex) ───────────
crypto.md5('hello')
crypto.sha256('hello')
crypto.sha512('hello')
crypto.sha3_256('hello')
crypto.sha3_512('hello')
crypto.blake2b_256('hello')
crypto.blake2b_512('hello')
// ── HMAC (key FIRST, then data) ─────────────────────────
crypto.hmac_sha256('my-secret-key', 'hello')
crypto.hmac_sha512('my-secret-key', 'hello')
// ── Encoding ────────────────────────────────────────────
crypto.base64_encode('Hello')
crypto.base64_decode('SGVsbG8=')
crypto.base58_encode('Hello')
crypto.base58_decode('...')
crypto.hex_encode('Hello')
crypto.hex_decode('48656c6c6f')
// ── Password hashing ────────────────────────────────────
// Note: compare functions take (hash, password) — hash FIRST
const hash = crypto.bcrypt_hash('myPassword123')
const match = crypto.bcrypt_compare(hash, 'myPassword123') // → true
const hash2 = crypto.argon2id_hash('myPassword123')
const match2 = crypto.argon2id_compare(hash2, 'myPassword123') // → true
// ── AES-256-GCM ─────────────────────────────────────────
// Function names: aes_gcm_encrypt / aes_gcm_decrypt
// Key MUST be exactly 32 bytes
const key = '12345678901234567890123456789012' // 32 bytes
const encrypted = crypto.aes_gcm_encrypt('sensitive data', key)
// → JSON string: {"n":"...","j":"...","t":"..."}
const decrypted = crypto.aes_gcm_decrypt(encrypted, key)
// → 'sensitive data'
// ── UUID v4 ─────────────────────────────────────────────
crypto.uuid() // → "550e8400-e29b-41d4-a716-446655440000"
}
Key crypto rules:
- HMAC: key first, data second →
hmac_sha256(key, data) - Compare: hash first, password second →
bcrypt_compare(hash, password) - AES names:
aes_gcm_encrypt/aes_gcm_decrypt(NOTencryptAES256GCM) - AES key must be exactly 32 bytes
HTTP client and constants
const http = require('http')
function onRequest() {
// ── Outbound HTTP request ───────────────────────────────
// http.send(method, url, headers, body) — 4 positional args
const resp = http.send('GET', 'https://myapp.t1y.net/timestamp', {}, '')
// resp: { status, statusCode, proto, headers, body, contentLength }
if (resp.statusCode !== http.StatusOK) {
return 'request failed'
}
return resp.body // auto-parsed if JSON
// POST with body
const resp2 = http.send(
'POST',
'https://api.example.com/data',
{ Authorization: 'Bearer token', 'Content-Type': 'application/json' },
JSON.stringify({ name: 'WangHua' })
)
}
Commonly used HTTP constants
// Methods
;(http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
// Status codes
;(http.StatusOK(200), http.StatusCreated(201), http.StatusBadRequest(400))
;(http.StatusUnauthorized(401), http.StatusForbidden(403), http.StatusNotFound(404))
;(http.StatusMethodNotAllowed(405), http.StatusConflict(409))
http.StatusInternalServerError(500)
// MIME types
;(http.MIMEApplicationJSON, http.MIMETextHTML, http.MIMETextPlain)
Read references/constants.md for the complete list.
File and environment operations (os module)
const os = require('os')
function onRequest() {
// ── Read file ───────────────────────────────────────────
// os.readFile(path, encoding, options)
// Supported encodings: 'utf-8', 'gb18030', 'gbk'
const content = os.readFile('/.private/config.txt', 'utf-8', {})
// With checksum verification
const verified = os.readFile('/.private/data.txt', 'utf-8', {
verify_checksum: true,
expected_checksum: '5d41402abc4b2a76b9719d911017c592',
})
// ── Write file ──────────────────────────────────────────
// os.writeFile(path, data, options)
// Options: { atomic, sync, return_checksum }
// Returns: { bytes_written, checksum }
// Simple write
os.writeFile('/.private/hello.txt', 'hello world', {})
// Atomic write (recommended — prevents partial reads)
const res = os.writeFile('/.private/data.json', JSON.stringify({ id: 1 }), {
atomic: true,
})
// res.bytes_written, res.checksum
// Write with checksum returned
const res2 = os.writeFile('/.private/data.txt', 'content', {
return_checksum: true,
})
// res2.checksum → md5 hex string
// Force fsync (critical data)
os.writeFile('/.private/log.txt', 'critical', { sync: true })
// ── Environment variables ────────────────────────────────
os.getEnv('API_KEY') // returns value or undefined
os.setEnv('API_KEY', 'sk-xxx') // set variable
os.unSetEnv('API_KEY') // delete variable
}
Security
Request signature verification
const ctx = require('context')
function onRequest() {
// ctx.sign() returns BOOLEAN (true/false). Does NOT throw.
if (!ctx.sign()) {
return '签名验证失败'
}
// Request is authentic — proceed
return '你好!'
}
Safe mode (AES-256-GCM encryption)
const ctx = require('context')
function onRequest() {
ctx.enableSafeMode()
// Request body is auto-decrypted, response is auto-encrypted
return { data: 'secure response' }
}
Read references/security.md for HMAC signing specification and detailed
security best practices.
Custom modules
Require path rules — critical
| File type | Path rule | Example |
|---|---|---|
.jsc (cloud function) |
Omit /.functions/ prefix — path relative to /.functions/ |
require("/pkg/response.jsc") |
.js (plain JavaScript) |
MUST use full path from root | require("/.librarys/crypto-js.min.js") |
.json |
MUST use full path from root | require("/.private/config.json") |
| Remote URL | Full HTTPS URL | require("https://cdn.example.com/lib.js") |
Why: .jsc files are pre-compiled and the platform resolves them relative
to /.functions/. .js and .json files are file-system imports that need the
absolute path.
⚠️ /.functions/ supports 2-level subdirectories. You can organize your
code like /.functions/api/users.jsc or /.functions/pkg/response.jsc.
Writing a helper module (.jsc)
// /.functions/pkg/response.jsc
const http = require('http')
// Use module.exports for helper modules (NOT for entry functions)
module.exports = {
fail: (code, message, data) => {
return { code, message, data }
},
success: (message, data) => {
return { code: http.StatusOK, message, data }
},
}
Using a helper module
// /.functions/index.jsc
// .jsc files: omit /.functions prefix
const resp = require('/pkg/response.jsc')
function onRequest() {
return resp.success('ok', null)
}
Importing JSON
// .json files: MUST use full path
const config = require('/.private/config.json')
function onRequest() {
return config.apiKey
}
Remote imports
const CryptoJS = require('https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js')
function onRequest() {
clearCache() // call only when you need to force re-fetch cached modules
return CryptoJS.MD5('Hello').toString()
}
- Local/remote imports are cached. Use
clearCache()to force refresh. - Remote imports: first load is slower, subsequent loads use cache.
.jsccloud function files are pre-compiled (not cached), changes take effect immediately.
SDK integration pattern (all platforms)
1. Install SDK → 2. Create client → 3. client.init() → 4. client.db.collection().operation()
// JavaScript/TypeScript example
import T1YOS from 't1yos'
const client = new T1YOS({
appId: 'your-app-id',
apiKey: 'your-api-key',
secretKey: 'your-secret-key',
})
await client.init()
const users = client.db.collection('users')
await users.insertOne({ name: 'Alice' })
Read references/sdks.md for platform-specific details.
MCP Server overview
URL: https://ai.t1y.net/mcp
Type: streamable-http
Required headers: X-T1Y-Application-ID, X-T1Y-API-Key, X-T1Y-Secret-Key,
X-T1Y-Access-Token
Tools: Database CRUD, env vars, file management, cloud functions, app settings, scheduled tasks (Pro).
Key tool for URL construction: Use app_info to get the user's Application
ID. This is essential for building correct cloud function URLs and debugging
access issues.
Read references/mcp.md for complete tool catalog with parameters.
Common mistakes checklist
- ❌ Using
.jsextension → ✅ Must be.jsc - ❌
function onRequest(ctx)→ ✅function onRequest()(no params) - ❌
ctx.method(property) → ✅ctx.method()(function) - ❌
ctx.query.name(property) → ✅ctx.query('name')(function) - ❌
ctx.body(property) → ✅ctx.body()(function) - ❌
ctx.headers['X'](property) → ✅ctx.get('X')(function) - ❌
ctx.setHeader(k, v)→ ✅ctx.set(k, v) - ❌
ctx.setCookie(name, value)→ ✅ctx.cookie({ name, value, ... }) - ❌
ctx.log(...)→ ✅console.log(...) - ❌
jwt.sign(payload, secret)→ ✅jwt.generateToken(userId, roles, minutes) - ❌
jwt.verify(token, secret)→ ✅jwt.verifyToken()(no args!) - ❌
collection.find(filter, { skip, limit })→ ✅collection.find(page, size, filter, sort) - ❌
insertOnereturns document → ✅ returnsObjectIDstring - ❌
updateOne(filter, { $set: data })→ ✅updateOne(filter, data)(no $set) - ❌
ctx.redirect(url, code)→ ✅ctx.redirect(code, url)(code first!) - ❌
ctx.sign()throws → ✅ctx.sign()returns boolean - ❌
crypto.encryptAES256GCM()→ ✅crypto.aes_gcm_encrypt() - ❌
hmac_sha256(data, key)→ ✅hmac_sha256(key, data)(key first!) - ❌
bcrypt_compare(password, hash)→ ✅bcrypt_compare(hash, password)(hash first!) - ❌ URL
https://myapp.t1y.net/test.jsc(missing AppID → 404) → ✅https://myapp.t1y.net/<AppID>/test.jsc— always include AppID in the URL path. Get AppID via MCPapp_infotool or ask the user. - ❌ Write code and stop without testing → ✅ After deploying, ALWAYS test with an HTTP request. Untested code is incomplete code.
- ❌
const user = users.findOne(...); if (!user)→ ✅ Wrap in try-catch —findOneTHROWS on no match, does NOT return null. - ❌ Deploy via file write + manual rebuild → ✅ Use MCP
function_create/function_update— auto-compiles, no manual rebuild. - ❌ require path
/.functions/pkg/x.jsc→ ✅require("/pkg/x.jsc")— omit/.functionsprefix for .jsc files.