when-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 2315d847681e
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 3e1cb86700a5e96238d7a2f1
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
policy.json
{
  "constants": {
    "AGENT_ADDRESS": "DS5hi7WsFdZ89fjZP9MVcGA8ooFNNQyQYvYvQ516CJid",
    "AGENT_ADDRESS_BOUND": true
  },
  "auth": {
    "anonymous": true
  },
  "polls/$pollId": {
    "description": "A scheduling poll. Readable by anyone who has opened its link (a join below); the random poll id is the link's secret. Only owners can change it. options is a JSON list of {id, start, end?, allDay?} with times in UTC seconds; all-day options start at midnight UTC of their date.",
    "fields": {
      "title": "String",
      "description": "String?",
      "location": "String?",
      "timezone": "String",
      "options": "String",
      "closed": "Bool",
      "chosen": "String?",
      "createdBy": "String!",
      "createdAt": "UInt!"
    },
    "rules": {
      "read": "@user.id != null && get(/polls/$pollId/joins/@user.id) != null",
      "create": "@user.id != null && @newData.createdBy == @user.id && @newData.createdAt <= @time.now && @newData.closed == false && @StringUtils.length(@newData.title) >= 1 && @StringUtils.length(@newData.title) <= 120 && @StringUtils.length(@newData.timezone) >= 1 && @StringUtils.length(@newData.timezone) <= 64 && @StringUtils.length(@newData.options) >= 2 && @StringUtils.length(@newData.options) <= 40000 && getAfter(/polls/$pollId/joins/@user.id) != null && getAfter(/polls/$pollId/owners/@user.id) != null && getAfter(/pollkeys/$pollId) != null",
      "update": "@user.id != null && get(/polls/$pollId/owners/@user.id) != null && @StringUtils.length(@newData.title) >= 1 && @StringUtils.length(@newData.title) <= 120 && @StringUtils.length(@newData.timezone) >= 1 && @StringUtils.length(@newData.timezone) <= 64 && @StringUtils.length(@newData.options) >= 2 && @StringUtils.length(@newData.options) <= 40000",
      "delete": "false"
    }
  },
  "polls/$pollId/joins/$userId": {
    "description": "Someone who opened the poll's link on a device. Knowing the poll id is what lets you join.",
    "fields": {
      "at": "UInt!"
    },
    "rules": {
      "read": "@user.id != null && @user.id == $userId",
      "create": "@user.id != null && @user.id == $userId && @newData.at <= @time.now && getAfter(/polls/$pollId) != null",
      "update": "false",
      "delete": "false"
    }
  },
  "polls/$pollId/owners/$userId": {
    "description": "Who can manage the poll: the creator, in the same batch that creates it, or anyone who presents the admin key.",
    "fields": {
      "at": "UInt!"
    },
    "rules": {
      "read": "@user.id != null && get(/polls/$pollId/joins/@user.id) != null",
      "create": "@user.id != null && @user.id == $userId && @newData.at <= @time.now && ((get(/polls/$pollId) == null && getAfter(/polls/$pollId).createdBy == @user.id) || getAfter(/pollclaims/$pollId/c/@user.id).key == get(/pollkeys/$pollId).key)",
      "update": "false",
      "delete": "false"
    }
  },
  "polls/$pollId/votes/$voteId": {
    "description": "One participant's answers. answers is a JSON object of option id to yes, maybe or no. Only the account that wrote a row can change it, and only while the poll is open; owners can remove a row.",
    "fields": {
      "name": "String",
      "answers": "String",
      "by": "String!",
      "updatedAt": "UInt"
    },
    "rules": {
      "read": "@user.id != null && get(/polls/$pollId/joins/@user.id) != null",
      "create": "@user.id != null && @newData.by == @user.id && getAfter(/polls/$pollId/joins/@user.id) != null && get(/polls/$pollId).closed == false && @newData.updatedAt <= @time.now && @StringUtils.length(@newData.name) >= 1 && @StringUtils.length(@newData.name) <= 60 && @StringUtils.length(@newData.answers) <= 40000",
      "update": "@user.id != null && @data.by == @user.id && @newData.by == @user.id && get(/polls/$pollId).closed == false && @newData.updatedAt <= @time.now && @StringUtils.length(@newData.name) >= 1 && @StringUtils.length(@newData.name) <= 60 && @StringUtils.length(@newData.answers) <= 40000",
      "delete": "@user.id != null && (@data.by == @user.id || get(/polls/$pollId/owners/@user.id) != null)"
    }
  },
  "polls/$pollId/comments/$commentId": {
    "description": "A comment on the poll, readable by everyone who has opened its link. Only its author or an owner can remove it; nobody can edit it.",
    "fields": {
      "name": "String",
      "text": "String",
      "by": "String!",
      "at": "UInt!"
    },
    "rules": {
      "read": "@user.id != null && get(/polls/$pollId/joins/@user.id) != null",
      "create": "@user.id != null && @newData.by == @user.id && getAfter(/polls/$pollId/joins/@user.id) != null && @newData.at <= @time.now && @StringUtils.length(@newData.name) >= 1 && @StringUtils.length(@newData.name) <= 60 && @StringUtils.length(@newData.text) >= 1 && @StringUtils.length(@newData.text) <= 1000",
      "update": "false",
      "delete": "@user.id != null && (@data.by == @user.id || get(/polls/$pollId/owners/@user.id) != null)"
    }
  },
  "pollkeys/$pollId": {
    "description": "The poll's admin key. Only the public API can read it, to check a key someone presents.",
    "fields": {
      "key": "String!"
    },
    "rules": {
      "read": "@user.id == @const.BOUNDED_PUBLIC_PRINCIPAL_API",
      "create": "@user.id != null && get(/polls/$pollId) == null && getAfter(/polls/$pollId).createdBy == @user.id && @StringUtils.length(@newData.key) >= 20",
      "update": "false",
      "delete": "false"
    }
  },
  "pollclaims/$pollId/c/$userId": {
    "description": "An admin key someone presented to become an owner. Never readable.",
    "fields": {
      "key": "String"
    },
    "rules": {
      "read": "false",
      "create": "@user.id != null && @user.id == $userId",
      "update": "@user.id != null && @user.id == $userId",
      "delete": "false"
    }
  },
  "functions": {
    "api": {
      "auth": "true",
      "entry": "functions/api.ts",
      "public": true,
      "methods": [
        "GET",
        "POST",
        "PATCH"
      ],
      "cors": "passthrough"
    }
  }
}

Change history

Quality pass: comments, CSV export, share the final time, calendar menu, fixes
2315d84768 · 4 hours ago
Bounded source sync
145324066b · 1 day ago
Bounded source sync
ad9b674dac · 1 day ago
Bounded source sync
e5b69608f3 · 1 day ago
Bounded source sync
a1685914b0 · 1 day ago
Bounded source sync
7108f42d4d · 1 day ago
Bounded source sync
ffb51b672e · 1 day ago
Bounded source sync
23b5d0c53b · 1 day ago