Files and Storage

This is a technical guide for people and agencies maintaining infrastructure for communities. It discusses how file storage and hosting is handled in the Qbix Platform ecosystem.

Hosting Web Apps

One Qbix Server can host many apps, each of which might service one or more communities. Qbix apps can share a database, but have custom configuration settings. Each app can have its own local/app.json which for example can be made readable and writable only by the specific operating system user behind the app, to upload its config there using FTP protocol and its own credentials. Or, you can use the nginx-upload-module or ngx_http_dav_module to support uploading via HTTP and that specific user credentials.

Depending on the URL (domain, subdomain, etc.) requested, nginx may load a different app, with a different local/app.json but the apps could share databases, directories, etc.

All apps would share the same PHP-FPM process manager pool, to execute their PHP scripts, and can re-use database connections too. Or, you can set up multiple PHP pools to each run under a different system user corresponding to that app. This would allow you to arrange user permissions to let different pools read files their corresponding local folders, including the secrets in those files.

Security and Isolation

The PHP-FPM processes are all isolated. You can choose to let them share database connections, and replace some of their directories like web/Q/uploads/Users with symlinks to the same locations on the file system.

But given vulnerabilities like Meltdown and Spectre, it might be better to host different Qbix apps on different machines. Then the Qbix Platform 3.0 allows communication through iframes, as well as periodic updates and sync via HTTP webhooks.

Thanks to CORS, servers running Qbix web services can also ship modules in third-party iframes which the embedding site can’t tamper with as long as the user is using a trusted browser. But the web services should still have quotas on operations, and limits on how many credits users can earn for their actions, just in case some user tries to access the servers with a hacked user-agent.

Requesting Files

The Q.proxies mechanism maps certain URL paths like Q/plugins/ to external URLs. This can be done, for instance, if multiple domains or subdomains reference the same JS, CSS, and image resources, eg from a CDN.

If each app has a different baseUrl, you will probably want to proxy the Q/uploads/ path to a CDN, so those URLs will be rewritten to reference URLs on the CDN instead (e.g. loading files from inside an S3 bucket). A fallback could be to symlink all the uploads folders for all apps on the same Qbix Server (they share the same mysql database) to one directory, which can itself be backed by Goofys (see below). But this approach isn’t so great for caching, because multiple URLs across multiple apps point to the same data. Better to use Q.proxies approach, even for uploaded content.

The CDN can cache resources from an origin server. Or we can use bundle.php to generate a bundle with a cache timestamp, and have people download it while installing an app in some App Store. This speeds up access to the files tremendously. Qbix automatically keeps track of the Q.ct (“cache timestamp”) to know whether to serve the cached file or a newer version.

Qbix also supports service workers installing web apps on the home screen, during which time it would download and unzip the bundle locally, and store the files in the local cache, to match requests. Kind of like a web app manifest used to be. Web apps added to the home screen support service workers. These same workers can also manage notifications sent by Qbix servers to those apps.

Static Files

static.php creates files with names like this:

  • web/foo/bar.landing.html – first hit, no cookies, serves everything inline
  • web/foo/bar.html – subsequent hits, with session cookie
  • web/foo/bar-ajax.json – static JSON data loaded with Javascript

Regular web servers like NGINX can then respond with bar.landing.html when the session cookie is absent, or bar.html for non-authenticated session. If sessionId cookie starts with Q_sessionId_authenticated_, however, (i.e. the user is logged in) then the regular PHP scripts would run instead of redirecting to static files.

Also for files under Q/uploads we call PHP to determine access and then get X-Accel-Redirect to load file from local server.

That PHP may also do 302 redirect to an Amazon S3 file, with the correct credentials in the URL or in a signed cookie!

Dynamic Requests

The PHP app server can handle all the dynamic AJAX requests, responding with JSON, or if it responds with HTML the entire page can be replaced and cached (and then the Javascript Q.Page.onLoad handler for that page has to run again).

Downloads / Streaming

We will have tools (audio/video players) that can load and play files from Vimeo / AWS / YouTube etc.

The external sites (eg Vimeo) would have our domains on a whitelist which can embed their players in an iframe (inside our tools). No one else would be able ti make this embed.

Our tools would check access using the usual Streams plugin, before creating the iframes with signed URLs that allow access to the audio / video / content.

We will have Streams/browser as a file manager allowing us to browse categories of streams, preview them and open the full stream players for images / videos / PDFs / audio / etc. Some streams will link to resources hosted externally on YouTube / AWS / Google Cloud Platform or even IPFS.


When uploading images, files and videos, we could process them on our server and then save them to Amazon S3 with goofys, mapped to a mounted volume. Similarly, we can read them from that volume. That allows us to have the same code for local infrastructure and cloud-backed storage. So people can install the Qbix servers anywhere, and they work out of the box.

But, often we don’t want to tie up our own bandwidth and CPU to process the file. In that case, we can build adapters for Vimeo, AWS, Google Cloud, Azure and other APIs to directly send (resumable) uploads to their cloud services, wait for processing (eg video) and obtain URLs for access to serve or stream files (including video).


People should be able to upload files to IPFS, and then pay external services for pinning these files. They can also manage entire directories this way, using IPNS, similar to Amazon S3 buckets, using

We would use API which is keyed by the domain, to upload files for free (eg for NFTs). The user can pay any additional pinning services to guarantee that the file will continue to be hosted at that cid.

The files are then loaded with HTTP (not streamed) using one of the subdomain gateways:

LBRY and Odysee

We can upload to LBRY same as IPFS. Odysee is just one streaming service / gateway same as IPFS pinning services / gateways.

This is instead of Vimeo if you want a self-sovereign option of hosting videos that can be streamed with HTTP RANGE requests, rather than just served as entire files with HTTP GET.


For managing valuable data, such as user balances of fungible and non-fungible tokens, votes, etc. we use Web3 wallets. Users can choose from one of many wallets (Metamask, Trust Wallet) to manage their private keys, instead of our platform.

Our websites bring up transactions for the user to understand and sign with his private keys.

Qbix websites allow users to tie web3 accounts to their user accounts, so other users can eg send them tokens.

We can authenticate users on our servers through any Web3 transaction they sign, such as a payment on an invoice or a vote in an election. As long as the transaction contains a challenge issued by our server (or our smart contract on the blockchain) and isn’t simply a replay of a previous transaction.

This may create a new user or tie a web3 wallet account to an existing user, similar to tying other platform xids like Telegram username, or email/mobile number.

Users have privacy controls for whether to reveal their mobile / email / xids including on web3, to other users. This affects their vcf cards (Add to Native Contacts) as well as profile buttons (eg whether you have an option to send tokens to their web3 account directly on the blockchain, in addition to internal credits in our centralized database which is much more confidential).