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.