DocumentationSelf hosted

Self hosted

The public version of PyCafe is hosted at py.cafe. However, this means your code and data is stored in our database. This is not always compatible with company policies. We also offer a self-hosted version of PyCafe that can be installed on your own infrastructure. This allows you to run PyCafe in your own environment, with your own branding, and with your own security policies.

NOTE: You can be completely self serving in deploying pycafe-server, and will not even need a license key to try it out.

If you are interested in becoming a design partner, please contact us or join us on discord.

Installation instruction

Once you contacted us, you will receive a Python wheel file that you can install on your own infrastructure.

Regular installation

Assuming you already have a Python environment, you can install PyCafe with the following command:

$ pip install ./pycafe_server-0.0.1-py3-none-any.whl

After installation, you can start the server with the following command:

$ PYCAFE_SERVER_UNSECURE_MODE_DONT_USE_IN_PRODUCTION=true uvicorn pycafe_server.asgi:app --port 8004 --host=0.0.0.0

We set the PYCAFE_SERVER_UNSECURE_MODE_DONT_USE_IN_PRODUCTION environment variable to true to indicate that this is not a production server. This does not require you to set up pycafe-server in the most secure way, but it is useful for testing purposes. If you remove this environment variable, the server will do extra security checks to make sure that the server is set up in a secure way, and will refuse to start if it is not.

Docker installation

If you are using Docker, you can create a Dockerfile with the following content, or base your own Dockerfile on this example:

FROM python:3.11
COPY ./pycafe_server-0.0.1-py3-none-any.whl pycafe_server-0.0.1-py3-none-any.whl
RUN pip install --no-cache-dir --upgrade pip
RUN pip install pycafe_server-0.0.1-py3-none-any.whl
EXPOSE 8004
ENV PYCAFE_SERVER_UNSECURE_MODE_DONT_USE_IN_PRODUCTION=true  # remove this line for production
ENTRYPOINT ["uvicorn", "pycafe_server.asgi:app", "--port=8004", "--host=0.0.0.0"]

You can build the docker image with the following command:

$ docker build -t pycafe-server .

And run the docker image with the following command:

$ docker run -it -p 8004:8004 pycafe-server

Reverse http proxy

Since the PyCafe server needs to run on https (or localhost, see next section), you will need to make sure that there is a reverse proxy in front of the server that handles the https connection. This is usually specific to your organization and out of the scope of this documentation.

Localhost

For testing purposes, you can run the server on localhost. This is not recommended for production, but it can be useful for testing purposes. Make sure you connect to http://localhost:8004 and not http://0.0.0.0:8004 or http://127.0.0.1:8004 as the browser will give special treatment to localhost and https, but not to the other addresses. This product is not finished yet, but if you are interested in this feature, please contact us or join us on discord.

Authentication

For evaluation purposes, you can skip authentication configure. However, for production use, you should configure authentication.

PyCafe support a reverse proxy that handles authentication, or support authentication via OAuth/Open ID Connect.

Proxy authentication

In case you run a reverse proxy server that handle authentication, the authentication info is often being pass to the pycafe-server via headers.

As an example, if you are using oauth2-proxy, you have two options.

Only pass the minimal information required for authentication, with optionally the email information:

PYCAFE_SERVER_PROXY_AUTH_HEADER_IDENTITY=x-forwarded-user
PYCAFE_SERVER_PROXY_AUTH_HEADER_EMAIL=x-forwarded-email

Or pass the full JWT token containing the user informationm, and the OIDC information to validate the token:

PYCAFE_SERVER_PROXY_AUTH_HEADER_USER_JWT=authorization
# used to check that the token we receive matches our 'audience'
PYCAFE_SERVER_CLIENT_ID=E1gs877o2osDBX2j4y8gsWFBrOv1QqUV
# used to validate the token
PYCAFE_SERVER_SERVER_METADATA_URL=https://dev-y02f2bpr8skxu785.us.auth0.com/.well-known/openid-configuration
# or, set the PYCAFE_SERVER_JWKS_URL to the URL of the JWKS endpoint
# PYCAFE_SERVER_JWKS_URL=https://dev-y02f2bpr8skxu785.us.auth0.com/.well-known/jwks.json

This assume that both these settings are set to true in your oauth2-proxy configuration:

....
pass_access_token = true
pass_authorization_header = true
...

In case of Amazon AWS ALB (Application Load Balancer), set either:

PYCAFE_SERVER_PROXY_AUTH_HEADER_IDENTITY=x-amzn-oidc-identity

Or for the full JWT token:

PYCAFE_SERVER_PROXY_AUTH_HEADER_USER_JWT=x-amzn-oidc-data
# needed to validate the jwt token
PYCAFE_SERVER_AWS_REGION=us-west-2

The advantage of providing the full JWT token is that the pycafe-server can provide more details in the logs (such as email) making it easier to identify users.

Auth using OAuth/Open ID Connect.

PyCafe supports authentication via OAuth/Open ID Connect. This is typically done by creating a new ‘application’ in your OAuth/Open ID Connect provider, and configuring the pycafe-server to use the client ID and client secret of this application.

And example configuration for Auth0 is as follows:

PYCAFE_SERVER_CLIENT_ID=E1gs877o2osDBX2j4y8gsWFBrOv1QqUV
PYCAFE_SERVER_CLIENT_SECRET=K6UrciHSbkBHy2Iq3n6rEiNeW8HsLbcTvQvmedwd5NhsNWydFyErRCCSdii90ukZ
PYCAFE_SERVER_SERVER_METADATA_URL=https://dev-y02f2bpr8skxu785.us.auth0.com/.well-known/openid-configuration
# generate your own using $ openssl rand -hex 32
PYCAFE_SESSION_SECRET_KEY=ec9f586ad063b9b6501ec766bae00377a8d9e0cca1c8e49d726e73c3b94fcf5e
# code_challenge_method not required, but recommended
PYCAFE_SERVER_CLIENT_KWARGS={'scope': 'openid profile email', 'code_challenge_method': 'S256'}
# We recommend using 'sub' as the user ID field, but you can use any field that is returned by the Open ID Connect provider
PYCAFE_SERVER_USER_ID_FIELD=email
# set these if you want to be able to logout
PYCAFE_SERVER_OAUTH_BASE_URL=https://dev-y02f2bpr8skxu785.us.auth0.com
PYCAFE_SERVER_OAUTH_LOGOUT_PATH=/v2/logout

In setting up your OAuth provider, the redirect, or callback URL should be http://localhost:8004/_authorize or https://pycafe.your.domain.com/_authorize depending if you are running on localhost or on a domain. The logout URL should be http://localhost:8004/_logout or https://pycafe.your.domain.com/_logout.

These values above can be used for evaluation purposes, and only works when accessing via http://localhost:8004. For production, you should replace these values with your OAuth/Open ID Connect provider.

In this example we use email as the user ID field, but you can use any field that is returned by the Open ID Connect provider, in general we recommend to use sub as the user ID field, since the sub field is unique for each user, and is not subject to change (unlike an email address).

Pre authentication

If you want to force users to authenticate before they can access the PyCafe server, you can set the PYCAFE_SERVER_PRE_AUTH environment variable to true. For example:

PYCAFE_SERVER_PRE_AUTH=True

This makes the whole application require authentication, and will redirect users to the authentication page if they are not authenticated.

Configuring users

Using environment variables

Editors

The easier way to configure users is to use environment variables.

You can set who is an editor by setting the PYCAFE_SERVER_EDITORS environment variable. This is a comma separated list of IDs. For example:

PYCAFE_SERVER_EDITORS="joe@py.cafe,jane@py.cafe"

Where we assume that PYCAFE_SERVER_USER_ID_FIELD=email, so that the email address is used as the user ID.

If you configure the PYCAFE_SERVER_USER_ID_FIELD to be sub, you should use the sub field as the user ID, for example:

PYCAFE_SERVER_USER_ID_FIELD=sub
PYCAFE_SERVER_EDITORS="1234567890,0987654321"

Admin

Use the PYCAFE_SERVER_ADMINS environment variable to set who is an admin. This is a comma separated list of IDs. For example:

PYCAFE_SERVER_ADMINS="james@py.cafe,jane@py.cafe"

Note that an admin is not automatically an editor, editors count towards the license limit, admins do not.

If you configure the PYCAFE_SERVER_USER_ID_FIELD to be sub, you should use the sub field as the user ID, for example:

PYCAFE_SERVER_USER_ID_FIELD=sub
PYCAFE_SERVER_ADMINS="1234567890,0987654321"

Configuring the database

This configuration can be skipped for evaluation purposes, but for production use, you should configure the database connection string.

The database connection string is configured using an environment variable PYCAFE_SERVER_DATABASE_URL, which is by default:

PYCAFE_SERVER_DATABASE_URL=sqlite:///./pycafe-server.db

Note that this default will store the database in a file pycafe-server.db in the current directory, which might be useful for testing, but not for production since if your server is deployed in a container, the database might be lost when the container is restarted.

NOTE: The database includes a unique instance ID, which is used to generate the license key. If you change the database, you will need to contact us to generate a new license key.

We use SQLAlchemy to connect to the database, so you can use any database that SQLAlchemy supports, see the SQLAlchemy documentation for more information about the connection string.

A typical connection string for a PostgreSQL database would look like this:

PYCAFE_SERVER_DATABASE_URL=postgresql+psycopg2://user:password@localhost/dbname

If this database connection string should be put together from other environment variables, you could construct the PYCAFE_SERVER_DATABASE_URL as follows:

export PYCAFE_SERVER_DATABASE_URL="postgresql+psycopg2://${DB_USER}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}"

Where you would set the DB_USER, DB_PASSWORD, DB_HOST, and DB_NAME environment variables to the correct values for your database, or replace the environment variable names to match those provided in your deployment environment.

Limiting the available frameworks

Using the environment variable PYCAFE_SERVER_FRAMEWORKS you can limit the frameworks that are available in the PyCafe environment. This is a comma separated list of framework names. For example:

PYCAFE_SERVER_FRAMEWORKS="streamlit,shiny,vizro,solara,dash,panel"

Is the default (all frameworks current available in PyCafe). If you only want to allow Streamlit and Dash, or just Virzo, you can set:

PYCAFE_SERVER_FRAMEWORKS="streamlit,dash"
PYCAFE_SERVER_FRAMEWORKS="vizro"

Disabling snippets

By default, our editor allows users to create snippets, which are like small projects that store the state in the browser history. If, for privacy reasons, or data security reasons, you want to disable snippets, you can do so by setting the PYCAFE_SERVER_ENABLE_SNIPPETS environment variable to false.

PYCAFE_SERVER_ENABLE_SNIPPETS=false

License key

For evaluation purposes, you can skip the license key configuration. However, for production use, you should obtain a license key from us. To generate a license key, we need your company name, the maximum number of editors, and the expiration date of the license (minimum 1 year). We also need your pycafe instance_id number, which you can obtain from the admin page at http://localhost:8004/admin or https://pycafe.your.domain.com/admin depending if you are running on localhost or on a domain.

NOTE: The license key includes a unique instance ID, which is used to generate the license key. If you change the database, you will need to contact us to generate a new license key. Make sure you configure the database before you request a license key.

The license key should be set in the PYCAFE_SERVER_LICENSE_KEY environment variable, for example:

PYCAFE_SERVER_LICENSE_KEY=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkY2Y2ZTAwZi0xNTc2LTQ1MzUtOWI2MC0wNWU3NjU0OTU3NDAiLCJtYXhfZWRpdG9ycyI6MSwiY3VzdG9tZXJfaW5mbyI6IlRlc3QgbGljZW5zZSIsImV4cCI6MTczMjkyNDgwMH0.ExMRqY12ZKX7blzdIFZRoVbCfKoErNQbFwhIh1b-V4uBEPrY7SUWA8G2FQ8LeRcJlWzjKppXLa0HoI1laN5hZrlnK2B09v_t68XfosR8bF_Qp2FqJjiH-pVRvdMGNoJNwRVvtkbI1bFR5jSGg6h2lt8LEBlGxwxv86G3B_EkbVkY0K_4ZkAMi7egMfB57-BbTw24SJ-DNKAEWMP2WdJuHc0k6nmEmIVdl5Q0zgL7CfmFbUWEw4uA-UC1ZjOF2XUR2kVR2wqTDK51U4P-OcEOW6a_rB7E58_mI592fOnJ6saeahstxAS7CkVt7SZXgdE-RaYB6N6B5jfrQzL3pgk11w

You can check the content of the license key by entering it at jwt.io. The payload should contain the following fields:

{
    'sub': 'dcf6e00f-1576-4535-9b60-05e765495740',  # this should match the instance_id or your pycafe-server
    'max_editors': 1,  # the maximum number of editors
    'customer_info': 'Test license',  # the name of your company
    'exp': datetime.datetime(2024, 11, 30, 0, 0)  # the expiration date of the license
}

Configuring code signing

This configuration can be skipped when PYCAFE_SERVER_UNSECURE_MODE_DONT_USE_IN_PRODUCTION=true, which is fine for evaluation purposes, but for production use, you should configure the code signing and not set this environment variable.

When exporting code from PyCafe to an HTML file, the code is signed to make sure that the code has not been tampered with. This is done using a private key that is stored on the server and a public key that is also send to the client You can configure the private key by setting the PYCAFE_SERVER_SIGN_PRIVATE_KEY environment variable, and the public key ny setting the PYCAFE_SERVER_SIGN_PUBLIC_KEY environment variable to a base64 encoded private and public key using a 2048 bit (RS256) RSA key. For example:

export PRIVATE_KEY=$(openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 | base64 -w 0)
export PUBLIC_KEY=$(openssl pkey -pubout -in <(echo "$PRIVATE_KEY" | base64 -d) | base64 -w 0)
echo $PRIVATE_KEY
echo $PUBLIC_KEY

Assign these values to the environment variables:

PYCAFE_SERVER_SIGN_PRIVATE_KEY=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2ZBVkVERXRtM05VTEgKRFZ0SzZvVUs4eHNTd0VXZXZIS0Y3ejMyYnNzKys4VDFmOXczVm43UzNlb2txemgrR2ZSRjNpNzB1QmxvMXRQTwp1Tyt1bWdqN1BuYVlER2pRU0xzTHU4eVUwTUxpb1o5blBaOFJGTHh6UTc1WDAzY1UvMk9JTWRoc2hPWEtmRlZrCjRPUXZEMmJVWFdyekZ6eGVuODg5cWxVL3cxNlBkOUo5ZDcwYnpVcWF0WFJXZU14b0dXKy9seFByKzlqMDdIVnQKSFFhdXBZZWVKSnAwWVRoS0htdjVCQ1h6RzNaZ0lyUlJ5cHlYdXZHcUR3NHhIZW1VY1RjZ1RGcktwWmkwWHQ4eApPUDFUZjg2dXRtVlc1TnBicGtXeG01Z205M0hXQW9laUJQN1FDODd1ZlJpU3JyeUVMV1orRUU3YnBTQVRYU3kvClBCdmkvY0twQWdNQkFBRUNnZ0VBS1FsYjhTRWlVMmhKamJuaXY5ajY4VEtBNER3RE84NkMrQklndWNNcWJWenAKSGpzYmlLU29JcC9uV0FOZ2x4cjNzamgvZ2VVcG1hY29Ed2dudzdZNWJ5NVZ5VFFhc1BhMFgzamVlZUhQQ2xLUQpJME1DRkpSM2RvQWJWZXRicG0yck1IRTgyL1VJb1ZPcGJsbWtTb2o5RHYzMUpqdnU5clRqOVpSS1lDUjUrU2laCkFXV3pFdjJmUlZqa2txcXBHckJybWMvQXpJOHBuNG1uMkpBQXgyaldxMVhCRG9ZQmYwSm5SVWhEcldJakRqb3AKbmJSWmk0SGlZUmhrd1FGekJ2ejhLZThmY2twa2pwTG5TM1IwZDF4N1UvdFVCdjFIVmE2MDFweWZIaFVDMzhUVApFTy8wQlBXTzdjbVphYy9LVHRqVHhVZ2NjQ21DMnhOL04wWGk5T1FZU1FLQmdRRGI2WUlpc3JuZDRNdVFXSkxhCmZhc2xoNGZSM1RXdGV5M1NqR2pwVFVDc0tEOUpRaDhPV201ekhOc0k1VTcyWGRmazhhMmN5RXdwSC9ZeVNJSU4KVG1KeHY1KzRGay92NnhCbzNxallSOU5ZQUpRSjF4VnRBQkJTRGpMaHY5K21SUzN5b3ZaeU16Z0E1TnYvUUdHZApLNzUvWW5FYVNyUStRaG11KzNXd0xySDdld0tCZ1FDNUdSd2hkOTRrNk1VMElBNGw1b2g0UWVDS2ZhL1R4VUE4Ckd1cWtkU0xsWXdBWjA2MHJzLzZ5eWJQVWVFdFQ5WjdmMkVJdTRpM1M4QkZTNllPTmNTSUJlME5OZUlXZ0xNRVIKd2U3YytVRTBLeFVDQTVib0h4cW1vOGZNRU9FN2xncWlDKy9QV0lyNW0yK0hDQjZmN1JOcVJRRDc5S2RXeGx4ZQprdjMra1B2L0t3S0JnUURDczdOc1ZTRUt1UVNDQUduRTJJNjI0NC8wMXcyM1V0LzNFaW13cFowa3oxMTQxdm40CkUwakcwajE3V3NyV2hhK29HL0hDWmZSSVZPdjlDUVZjRXBKR2M0cE55ak5zRDRxdnpyTmZDZmlzYVphb1A2M1EKbThYWFlJcmNKSzAyNFBrVllnTWdpUWlFZ2h2bG1uTFNYUENFaC92cmMwaXg5b1FXMEx3YUNMMjhFUUtCZ0JQMgpYVTVPdXlxSDRCekxDRHVXTFVtcThTaCtmYVA5TmZ4RmV1RFpzVFhFQTVMRmZYdWxJZWJ1VWZLc05wT0xmZUx1ClU5VHlsN2FVSllvbm5RUjRHdzdGMkhCV2V6TFhJTXRsZXZsaCtHSkQ4elFRWndvSnNMb2dGOEF1WWR3blJtWkwKbTBWbGF6ZVlSK0xzUVVGR05EM1Z5MUZoQlZzYWNZUFJub3NCMyt1WkFvR0FRMGFsdU90a3NOZkVJRHRicmdRSQppbHo5REVuVHJHQ2M3WXhjSVpaNzZBb2RrRi9uZmJtaTRiVUtSTVZEZDVxV3IrNHBrbnhyb3FTN0pQcHl6VFU2Cmp3M25uZlZpSUF1U3hwcmExUGY1WTBBZFBmTWZOVmo4VFdLM0xLRjVhMWFVN3hJeDVta1AxNlZqMG96cHhPcW8KMHdzTDNPamEycEJORnRCZnN3OEhqblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
PYCAFE_SERVER_SIGN_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFud0ZSQXhMWnR6VkN4dzFiU3VxRgpDdk1iRXNCRm5yeHloZTg5OW03TFB2dkU5WC9jTjFaKzB0M3FKS3M0ZmhuMFJkNHU5TGdaYU5iVHpyanZycG9JCit6NTJtQXhvMEVpN0M3dk1sTkRDNHFHZlp6MmZFUlM4YzBPK1Y5TjNGUDlqaURIWWJJVGx5bnhWWk9Ea0x3OW0KMUYxcTh4YzhYcC9QUGFwVlA4TmVqM2ZTZlhlOUc4MUttclYwVm5qTWFCbHZ2NWNUNi92WTlPeDFiUjBHcnFXSApuaVNhZEdFNFNoNXIrUVFsOHh0MllDSzBVY3FjbDdyeHFnOE9NUjNwbEhFM0lFeGF5cVdZdEY3Zk1UajlVMy9PCnJyWmxWdVRhVzZaRnNadVlKdmR4MWdLSG9nVCswQXZPN24wWWtxNjhoQzFtZmhCTzI2VWdFMTBzdnp3YjR2M0MKcVFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==

Make sure the private key is kept secret, possibly in a secure vault.

;;