The API documentation for a Tiny node.
Root features land under the root api route, without needing a context scope.
Core features land under the root api route, and are as follows:
- GET
/type- Returns the type of node, which can behome,file, ordb. - GET
/self/avatar?- Returns null if unauthenticated, or the user object if authenticated- If an app, also returns app data (such as preferred nodes, if the node is a home)
- If avatar is specified, instead return your avatar image
- DELETE
/self- If an app, deletes app data
- If a user, deletes all user data -- requires a password string in the text body to confirm
- DELETE
/self/avatar- As a user, delete your avatar - GET
/users/:username/avatar?- Get a user's public information, or ifavataris specified, just the user's avatar
All URLs land under the /auth parent route, e.x.:
POST /auth/loginGET /auth/handshake/start
The routes are as follows:
Core:
- POST
/login- Post with a body of{ username: string; password: string }and either get a 403 or a session - POST
/register- Post with a body of{ username: string; password: string }and get a 401 or a 204. - GET
/can-register- Returns a 204 or 403 as to whether or not registrations are open on this node. - POST
/change-pass- Post with a body of{ username: string; password: string; newpass: string }and either get a 403 or a 204. - GET
/sessions- Receives a list of active sessions - DELETE
/session/:id?- Revoke all or one session via an ID - GET
/apps- If a user, gets a list of connected apps, and, if a home, their preferred file and db storage nodes
- If an app, get only a list containing your information
- DELETE
/apps/:id- as a User, delete an app and it's information. If the query parameter?softis specified, then just remove its saved configuration, but keep the data. - POST
/logout- Logout of the currently authorized session - GET
/refresh- Refresh the current session - revokes the old session and returns a new ID
Handshakes:
- GET
/handshake/start- Start a handshake by redirecting to this route with the?app={appId}&redirect={redirectUrl}params- Optionally,
collectionsandpermissionsparameters can be added as well.
- Optionally,
- POST
/handshake/complete- Finish a handshake by posting{ app, redirect, code, collections?, permissions? }to this route and getting a session back (if they matched the initial handshake parameters + the returned code correctly) - GET
/handshake/:id- As a user, get handshake information - POST
/handshake/:id/(approve|cancel)- As a user, approve or cancel the handshake and be given back a url to redirect to.
Master Keys:
- GET
/master-key/:id?- Get all or get one master key via an ID - POST
/master-key- Add a master-key to the Tiny node- For a dedicated node, the body can be empty or have a
{ name: string }object and will return a text body of the master key id - For a home node, the body must be an
{ key: string, url: string, type: 'file' | 'db', name?: string }object, and will return a 204
- For a dedicated node, the body can be empty or have a
- PUT | DELETE
/master-key/:id- Update the master key with a body of{ name: string }, or remove a master-key - POST
/master-key/:id/generate-session- Use a master-key to generate a session (on a dedicated node)
Scoped features land under the /:feature/:context/:identifier route, with:
:featurebeing the feature id or name:contextisuser,secure, or an app domain (i.e.app.coorexample.app).:identifieris a username if the context isuseror an app domain, or a secure hash if the context issecure.
For example:
GET /files/user/john.smith/public/profile/avatar.pnghas ausercontext and referencesjohn.smith, and so will query john.smith's global public profile avatar, found under/public/profile/avatar.pngGET /db/cool.app/BobTest/dynamic/graphql?query={+shoppingCart{+items+{+id,+name,+price+},+quantity+}+}has acool.appcontext and referencesBobTest, and so will use BobTest's cool.app dynamic database to run a graphql query on
If context or identifier is a ~, then assume it from the given session, so calling routes like
GET /files/~/~/private/config.json and GET /files/~/bob.test/public/posts/1.json is simpler from an app.
All URLs land under the /files/:context/:identifier parent route, e.x.:
GET /files/app.co/john.smith/public/notes/1.jsonDELETE /files/user/BobTest/root/appdata/cool.app/blob.json
Paths are created with a Root Identifier and then the path itself. Root Identifiers can be one of three types:
publicis the public folder of the context;- For apps, it's
/public/appdata/{app domain} - For secure apps, it's
/public/appdata/secure/{hash} - For users, it's
/public
- For apps, it's
privateis the private folder of the context:- For apps, it's
/appdata/{app domain} - For secure apps, it's
/appdata/secure/{hash} - For users, it's
/private
- For apps, it's
rootis the root accessor, which is always/collectionsis the collection accessor, which will be/collections/{path}
The routes are as follows:
- GET | PUT | DELETE
/:root(public|private|root|collections)/:path(.+)- GET to read the file
- PUT to write (over) a file
- DELETE to remove a file
- GET
/list-files/:path(.*)?- List file paths for the context container- Optionally define a path to list the files under
- Use
?page={number}to navigate to a particular page - Use the
?advance=truequery to return file metadata instead of just path names
- GET
/storage-stats- Get the storage stats for the context container- Returns
{ user: number, available?: number, max?: number }, with each value being the number of bytes
- Returns
- POST
/batch-info- Get information for the file paths listed in the json (as relative to the context)- Should be given a JSON string array
- Can be accessed without an authorized session, but will only return information under the
publicfolder - Returns a list of file metadata (similar to
/list-files?advance=true)
All URLs land under the /db/:context/:identifier parent route, e.x.:
GET /db/app.co/john.smith/dynamic/graphql?query={hello}PUT /db/user/BobTest/key-value/store/foo
The Key-Value DB was the initial database store made for the Tiny suite, which simply gives access to a persistent simple storage through a REST like API.
- GET | PUT | DELETE
/key-value/store/:key- GET to get the value for the given key
- PUT to set the value
- DELETE to remove the value from the store
- POST
/key-value/store- Add an object to the store with a unique key (value must be an object) - POST
/key-value/search- Search the store given a search query object - POST
/key-value/batch- Batch PUT / DELETE actions on the store
The Dynamic DB was added to create some more flexibility and type structure for app databases, along with easier querying and storage for complicated objects.
- GET | PUT | DELETE
/dynamic/tables/:name- GET to get the schema of a table
- PUT to update/replace the schema of a table (keeps the data if fields are the same)
- DELETE to drop a table
- GET | POST | DELETE
/dynamic/tables- GET to get all the schemas of the dynamic db
- POST to add a table
- DELETE to drop all tables
- GET | POST
/dynamic/graphql- Interact (query, mutation, etc.) with the database using graphql
The GraphQL schema is as follows:
# Tiny-defined Global Types
# A javascript Date
type Date
# Always null
type Void
# Any
type JSON
# A batch option
enum Operation { add, put, del }
# Options to use in a search query
input SearchOptions {
start: String
end: String
skip: Int
limit: Int
query: JSON
projection: [String]
sort: String
}
# Passed as a stub type -- we're not defining this right now
type ChildType {
id: ID!
value: JSON
}
###
# The type we are defining
type Type {
# This is always present, regardless if its in your passed schema or not,
# and it will always be an `ID!` type
id: ID!
# Some sample fields
child: Child
foo: Boolean
counter: Int!
}
# The generated input type
input TypeInput {
# Takes an ID instead of an entire object
child: ID
# Same as usual
foo: Boolean
# Doesn't keep the required flag
counter: Int
}
# The generated batch input type
input TypeBatchInput {
type: Operation!
id: ID!
value: TypeInput
}
# The generated batch return type
type TypeBatchReturn {
type: Operation!
id: ID!
value: Type
}
# Generated Queries
type Query {
# Get many items (or all items)
types(search: SearchOptions): [Type]!
# Get one item
type(id: ID!): Type
}
# Generated Mutations
type Mutation {
# Batch puts/deletes
batchType(input: [TypeBatchInput]!): [TypeBatchReturn]!
# Add an item
addType(input: TypeInput!): Type!
# Update an item
putType(id: ID!, input: TypeInput!): Type!
# Delete an item
delType(id: ID!): Void
}
# Generated Subscriptions (currently unimplemented)
type Subscription {
# Subscribe to the type, or reduce it to a singular item of the type / an operation on that type
type(id: ID, op: Operation): Type
}
As required by the Gaia implementation spec, with the read url being under /gaia/read/:address/:path(.+).
Association Tokens are required so the node knows where to store the data. User Addresses must be registered
to a user on the node before using as there is no way to do auto-registration.
Files are stored under /{user}/gaia/{address}/{...path} directory.
- HEAD | GET
/gaia/read/:address/:path(.+) - GET
/gaia/hub_info - POST
/gaia/store/:address/:path(.+) - DELETE
/gaia/delete/:address/:path(.+) - POST
/gaia/revoke-all/:address - POST
/gaia/list-files/:address
It's fairly straightforward:
- GET
/.well-known/webfingerto run the WebFinger request?resource={username}@{domain}will either search for the user if the domain is local, or redirect tohttps://{domain}/.well-known/webfinger?resource={username}@{domain}if the domain is remote?resource={username}-{app}@{domain}will search for the user and retrieve only the aliases and links that belong to the specified app?resource={secure-hash}-secure@{domain}will search for the user by the hash and retrieve only the aliases and links that belong to the specified hash
- PUT
/.well-known/webfingerwith an authorized session to store WebFinger information for the authorized application or for the root user- You must give the entire body -- partial updates are not allowed, and if the array is empty it will remove the information completely.
- A User session may specify other contexts via
?context={context}&identifier={identifier}, withidentifieronly being required if the context issecure.
Not yet implemented.
Not yet implemented.
Draft:
- GET
/friends- Get friends - GET
/friends?of- Get users who have you as a friend - GET
/friends/:username- Get a user's friends - GET
/friends/:username?of- Get users who have the specified user as a friend - POST
/friends/:username- Add a friend - DELETE
/friends/:username- Remove a friend - PUT
/friends/notify- Adjust whether or not you want notifications for friendship changes