tasks-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 9b0d0f7b9a93
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 f6f2112e77bc8c011e38c83a
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
{
  "auth": {
    "anonymous": true
  },
  "defs": {
    "joined": "get(/boards/$boardId/joins/@user.id) != null",
    "owner": "get(/boards/$boardId/owners/@user.id) != null && get(/boards/$boardId/joins/@user.id) != null",
    "canRead": "@user.id != null && get(/boards/$boardId/joins/@user.id) != null && get(/boards/$boardId) != null",
    "canWrite": "@user.id != null && getAfter(/boards/$boardId/joins/@user.id) != null && getAfter(/boards/$boardId).archived == false",
    "title120": "@StringUtils.length(@newData.title) >= 1 && @StringUtils.length(@newData.title) <= 360",
    "cardValid": "@StringUtils.length(@newData.title) >= 1 && @StringUtils.length(@newData.title) <= 1500 && @StringUtils.length(@newData.description) <= 60000 && (@StringUtils.length(@newData.due) == 0 || @StringUtils.length(@newData.due) == 10 || @StringUtils.length(@newData.due) == 16) && @StringUtils.length(@newData.labels) <= 2000 && @StringUtils.length(@newData.assignees) <= 4000 && @StringUtils.length(@newData.checklist) <= 160000 && @StringUtils.length(@newData.cover) <= 12 && getAfter(/boards/$boardId/lists/@newData.listId) != null"
  },
  "boards/$boardId": {
    "description": "A board. Readable by anyone who has opened its link (a join below); the random board id is the link's secret. Only owners (the creator, or whoever presents the admin key) can rename, archive or delete it. An archived board is read only.",
    "fields": {
      "title": "String",
      "archived": "Bool",
      "createdBy": "String!",
      "createdAt": "UInt!"
    },
    "rules": {
      "read": "@user.id != null && @def.joined",
      "create": "@user.id != null && @StringUtils.length($boardId) >= 16 && @newData.createdBy == @user.id && @newData.createdAt <= @time.now && @newData.archived == false && @def.title120 && getAfter(/boards/$boardId/joins/@user.id) != null && getAfter(/boards/$boardId/owners/@user.id) != null && get(/boardkeys/$boardId) == null && getAfter(/boardkeys/$boardId) != null",
      "update": "@user.id != null && @def.owner && @def.title120",
      "delete": "@user.id != null && @def.owner"
    }
  },
  "boards/$boardId/joins/$userId": {
    "description": "A device that opened the board's link. Knowing the board id is what lets you join, unless an owner removed that device. memberId is the name the device picked (You are ...); via is 'api' for the public API.",
    "fields": {
      "at": "UInt!",
      "memberId": "String?",
      "via": "String?"
    },
    "rules": {
      "read": "@user.id != null && (@user.id == $userId || @def.owner)",
      "create": "@user.id != null && @user.id == $userId && @newData.at <= @time.now && getAfter(/boards/$boardId) != null && get(/boards/$boardId/blocks/$userId) == null && (@newData.via == null || @user.id == @const.BOUNDED_PUBLIC_PRINCIPAL_API) && (@newData.memberId == null || getAfter(/boards/$boardId/members/@newData.memberId) != null)",
      "update": "@user.id != null && @user.id == $userId && @newData.via == @data.via && (@newData.memberId == null || getAfter(/boards/$boardId/members/@newData.memberId) != null)",
      "delete": "@user.id != null && (@user.id == $userId || @def.owner)"
    }
  },
  "boards/$boardId/blocks/$userId": {
    "description": "A device an owner removed from the board. It cannot join again.",
    "fields": {
      "at": "UInt!"
    },
    "rules": {
      "read": "@user.id != null && (@user.id == $userId || @def.owner)",
      "create": "@user.id != null && @def.owner && @user.id != $userId && $userId != @const.BOUNDED_PUBLIC_PRINCIPAL_API && @newData.at <= @time.now",
      "update": "false",
      "delete": "@user.id != null && @def.owner"
    }
  },
  "boards/$boardId/owners/$userId": {
    "description": "Who can rename, archive and delete the board and remove people: the creator, in the same batch that creates it, or anyone who presents the admin key (which must exist). Owner powers also need the device's join, so removing a device ends them on that device. Owner records go only once the board is deleted. The admin key itself is never deleted, and a board is created only with a new key, so a deleted board's id cannot be used again.",
    "fields": {
      "at": "UInt!"
    },
    "rules": {
      "read": "@user.id != null && (@user.id == $userId || @def.owner)",
      "create": "@user.id != null && @user.id == $userId && @newData.at <= @time.now && ((get(/boards/$boardId) == null && getAfter(/boards/$boardId).createdBy == @user.id) || (get(/boardkeys/$boardId) != null && getAfter(/boardclaims/$boardId/c/@user.id).key == get(/boardkeys/$boardId).key))",
      "update": "false",
      "delete": "@user.id != null && getAfter(/boards/$boardId) == null && (@user.id == $userId || @def.owner)"
    }
  },
  "boardkeys/$boardId": {
    "description": "The board'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(/boards/$boardId) == null && getAfter(/boards/$boardId).createdBy == @user.id && @StringUtils.length(@newData.key) == 24",
      "update": "false",
      "delete": "false"
    }
  },
  "boardclaims/$boardId/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 && @StringUtils.length(@newData.key) == 24",
      "update": "@user.id != null && @user.id == $userId && @StringUtils.length(@newData.key) == 24",
      "delete": "false"
    }
  },
  "boards/$boardId/lists/$listId": {
    "description": "A list (column) on the board. pos orders lists left to right. Anyone on the board can add, rename, move and archive lists; only owners delete them, when deleting the board.",
    "fields": {
      "title": "String",
      "pos": "Float",
      "archived": "Bool",
      "createdAt": "UInt!"
    },
    "rules": {
      "read": "@def.canRead",
      "create": "@def.canWrite && @StringUtils.length($listId) >= 8 && @newData.createdAt <= @time.now && @def.title120",
      "update": "@def.canWrite && @def.title120",
      "delete": "@user.id != null && @def.owner"
    }
  },
  "boards/$boardId/cards/$cardId": {
    "description": "A card. listId is its list and pos its place in it. due is '' or a wall-clock date ('2026-10-30' or '2026-10-30T15:00'). labels and assignees are JSON lists of label and member ids; checklist is a JSON list of {id, text, done}. Anyone on the board can add, edit, move and archive cards, and delete archived ones.",
    "fields": {
      "listId": "String",
      "title": "String",
      "description": "String",
      "pos": "Float",
      "due": "String",
      "done": "Bool",
      "labels": "String",
      "assignees": "String",
      "checklist": "String",
      "cover": "String",
      "archived": "Bool",
      "createdBy": "String!",
      "createdAt": "UInt!"
    },
    "rules": {
      "read": "@def.canRead",
      "create": "@def.canWrite && @StringUtils.length($cardId) >= 8 && @newData.createdBy == @user.id && @newData.createdAt <= @time.now && @def.cardValid",
      "update": "@def.canWrite && @def.cardValid",
      "delete": "@user.id != null && (@def.owner || (@def.joined && @data.archived == true && get(/boards/$boardId).archived == false))"
    }
  },
  "boards/$boardId/labels/$labelId": {
    "description": "A label people can put on cards: a name (may be empty) and a color.",
    "fields": {
      "name": "String",
      "color": "String",
      "createdAt": "UInt!"
    },
    "rules": {
      "read": "@def.canRead",
      "create": "@def.canWrite && @newData.createdAt <= @time.now && @StringUtils.length(@newData.name) <= 120 && @StringUtils.length(@newData.color) >= 3 && @StringUtils.length(@newData.color) <= 12",
      "update": "@def.canWrite && @StringUtils.length(@newData.name) <= 120 && @StringUtils.length(@newData.color) >= 3 && @StringUtils.length(@newData.color) <= 12",
      "delete": "@user.id != null && (@def.owner || (@def.joined && get(/boards/$boardId).archived == false))"
    }
  },
  "boards/$boardId/members/$memberId": {
    "description": "A person on the board, by name, for assigning cards and signing comments. Names are not accounts. Anyone on the board can add or rename people; only owners can remove someone.",
    "fields": {
      "name": "String",
      "removed": "Bool",
      "createdAt": "UInt!"
    },
    "rules": {
      "read": "@def.canRead",
      "create": "@def.canWrite && @newData.createdAt <= @time.now && @newData.removed == false && @StringUtils.length(@newData.name) >= 1 && @StringUtils.length(@newData.name) <= 120",
      "update": "@def.canWrite && @StringUtils.length(@newData.name) >= 1 && @StringUtils.length(@newData.name) <= 120 && (@newData.removed == @data.removed || @def.owner)",
      "delete": "@user.id != null && @def.owner"
    }
  },
  "boards/$boardId/comments/$commentId": {
    "description": "A comment on a card. by is the account that wrote it and name the name it signed with. Authors edit their own. Authors and owners delete comments, and anyone on the board deletes the comments of a card they delete in the same batch.",
    "fields": {
      "cardId": "String!",
      "text": "String",
      "by": "String!",
      "name": "String",
      "createdAt": "UInt!",
      "editedAt": "UInt?"
    },
    "rules": {
      "read": "@def.canRead",
      "create": "@def.canWrite && @newData.by == @user.id && @newData.createdAt <= @time.now && @StringUtils.length(@newData.text) >= 1 && @StringUtils.length(@newData.text) <= 15000 && @StringUtils.length(@newData.name) >= 1 && @StringUtils.length(@newData.name) <= 120 && getAfter(/boards/$boardId/cards/@newData.cardId) != null",
      "update": "@def.canWrite && @data.by == @user.id && @StringUtils.length(@newData.text) >= 1 && @StringUtils.length(@newData.text) <= 15000 && @StringUtils.length(@newData.name) >= 1 && @StringUtils.length(@newData.name) <= 120",
      "delete": "@user.id != null && (@def.owner || (@def.joined && get(/boards/$boardId).archived == false && (@data.by == @user.id || getAfter(/boards/$boardId/cards/@data.cardId) == null)))"
    }
  },
  "functions": {
    "api": {
      "auth": "true",
      "entry": "functions/api.ts",
      "public": true,
      "methods": [
        "GET",
        "POST",
        "PATCH",
        "DELETE"
      ],
      "cors": "passthrough"
    }
  },
  "constants": {
    "AGENT_ADDRESS": "CcbRBgYJRacUe2E4N1W3hZiZwQPP5iJv6zGdsFf7pwWF",
    "AGENT_ADDRESS_BOUND": true
  }
}

Change history

Point OpenApps links to openapps.free
9b0d0f7b9a · 17 hours ago
Bind agent address in policy
25f84633c0 · 19 hours ago
Use OpenApps service URLs and add contribution strip
07519eb348 · 19 hours ago
Bounded source sync
3296872001 · 1 day ago
Bounded source sync
ac8d2f5d18 · 1 day ago
Bounded source sync
2a5b08a001 · 1 day ago
Bounded source sync
d2242ddf34 · 1 day ago