Deploy the single-server MaaS and PriceTag pilot¶
This runbook deploys the real-provider gateway and a separate mock-only performance gateway on RHEL. Each host runs Praxis, the standalone MaaS API, PriceTag metering and PostgreSQL as rootless Quadlet services. Only Praxis HTTPS on TCP 8443 is public. MaaS and PostgreSQL are private; MaaS also has a loopback-only host port for administrator scripts.
The standalone MaaS runtime and the Praxis image with api_key_auth and
external_metering are prerequisites. Use reviewed immutable source/image
revisions. This deployment uses finite MaaS API keys and PriceTag USD budgets;
it does not configure Praxis token_rate_limit. The gateway-to-metering M2M
bearer credential is stored as a Podman secret; MaaS and PostgreSQL remain
private. Do not expose the MaaS port.
1. Launch the RHEL hosts¶
Run on an AWS administrator workstation from a reviewed checkout of
redhat-et/secure-single-server. The AWS helper creates encrypted root disks
and security groups restricted to the workstation's public IPv4 /32.
Choose the source checkout and AWS values for your environment:
cd /absolute/path/to/secure-single-server
source scripts/aws/session.sh
aws_test_credentials
REGION=eu-central-1
SUBNET=''
CLIENT_CIDR=''
RUN_PREFIX="pricetag-$(python3 -c 'import uuid; print(uuid.uuid4().hex[:8])')"
SSH_KEY="$HOME/.secure-single-server-tests/ssh/$RUN_PREFIX"
aws_test_discover
aws_test_key
ssh-add "$SSH_KEY"
PRICETAG_VM=(configs/aws/pricetag-cloud.json --scenario remote-gateway \
--ssh-access restricted --https-access restricted)
Review and launch the real-provider server:
aws_test_plan pricetag-real "${PRICETAG_VM[@]}"
aws_test_deploy pricetag-real "${PRICETAG_VM[@]}"
PRICETAG_INSTANCE=$(jq -er '.InstanceId' \
".state/$RUN_PREFIX-pricetag-real.json")
aws --region "$REGION" ec2 wait instance-running --instance-ids "$PRICETAG_INSTANCE"
aws_test_verify pricetag-real
Launch a second VM for performance work. Never configure real upstream credentials on this host:
aws_test_plan pricetag-perf "${PRICETAG_VM[@]}"
aws_test_deploy pricetag-perf "${PRICETAG_VM[@]}"
PRICETAG_INSTANCE=$(jq -er '.InstanceId' \
".state/$RUN_PREFIX-pricetag-perf.json")
aws --region "$REGION" ec2 wait instance-running --instance-ids "$PRICETAG_INSTANCE"
aws_test_verify pricetag-perf
aws_test_verify prints the current SSH host, key path and journal for the
selected VM. If the instance is still pending, rerun the waiter and verify;
never rerun deploy for an instance that already exists. Keep both journals.
The 32-GiB, 150-GiB instance profile is a test starting point, not a capacity
claim. See AWS operations for restoring a launch session,
printing both VM details, and changing ingress.
2. Copy the reviewed server branch and connect¶
Repeat for each VM. On the workstation, select and verify the destination:
Copy the tracked source from the reviewed branch, then connect:
(
set -o pipefail
git ls-files -z --cached --others --exclude-standard | tar --no-xattrs --null -T - -czf - | \
ssh -o IdentitiesOnly=yes -o ForwardAgent=no -i "$SSH_KEY" "$RHEL_HOST" \
'mkdir -p ~/secure-single-server && tar -xzf - -C ~/secure-single-server'
)
ssh -o IdentitiesOnly=yes -o ForwardAgent=no -i "$SSH_KEY" "$RHEL_HOST"
For the second host, return to the workstation and set VM_NAME=pricetag-perf,
then repeat. This transfers tracked and new, non-ignored source files from the
current checkout, including local edits. Review the diff before copying; do not
include credential files or .state data.
3. Prepare RHEL and obtain pinned images¶
Run on each RHEL host after copying the source:
sudo dnf install -y podman git python3 python3-pyyaml openssl \
policycoreutils-python-utils shadow-utils
cd ~/secure-single-server
sudo scripts/common/install --prepare
Build the MaaS API from the reviewed standalone-runtime commit and build
unmodified PriceTag metering from the revision pinned by this repository. The
MaaS checkout must include maas-api/Dockerfile and maas-api/.
The minimal runtime branch includes the prerequisite
standalone API-key PR #1548
plus the runtime adaptation for operation without Kubernetes.
The commands pin the reviewed runtime commit; they do not build from a moving branch tip:
git clone --branch feat/standalone-runtime-minimal \
https://github.com/Ladas/models-as-a-service.git ~/models-as-a-service
git -C ~/models-as-a-service checkout --detach 6ac4c80ebc95f590e5f24593ac15bb80c7f9fa1d
sudo scripts/pricetag/build-maas --source ~/models-as-a-service \
--expected-commit 6ac4c80ebc95f590e5f24593ac15bb80c7f9fa1d
git clone https://github.com/redhat-et/pricetag-metering.git ~/pricetag-metering
sudo scripts/pricetag/build-metering --source ~/pricetag-metering
Pull the published combined Praxis image. This multi-architecture digest
contains the reviewed redhat-et/praxis-ai integration and experimental
filters from source revision 5667c4acecd0981ae5114cbbf2ee3dcccec9c248.
Praxis no longer needs a local image build; MaaS and metering still do.
PRAXIS_IMAGE=quay.io/opendatahub/praxis-experimental@sha256:502f120fed7b8e516ce1d78396045c8f72673672351432b9dc2e61baf5c13ac8
sudo podman pull "$PRAXIS_IMAGE"
sudo podman image inspect "$PRAXIS_IMAGE" \
--format '{{index .Labels "org.opencontainers.image.revision"}}'
The revision must match the source above. See the publication and smoke-test workflow.
Import all three local images into the rootless service account and capture
their immutable IDs. Set PRAXIS_IMAGE to the image built or pulled above:
set -o pipefail
PRICETAG_UID=$(id -u praxis-svc)
svc() {
(cd /tmp && sudo -u praxis-svc env \
XDG_RUNTIME_DIR="/run/user/$PRICETAG_UID" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$PRICETAG_UID/bus" "$@")
}
PRAXIS_IMAGE=quay.io/opendatahub/praxis-experimental@sha256:502f120fed7b8e516ce1d78396045c8f72673672351432b9dc2e61baf5c13ac8
sudo podman save "$PRAXIS_IMAGE" | svc podman load
sudo podman save localhost/pricetag-maas:standalone | svc podman load
sudo podman save localhost/pricetag-metering:upstream-557ceb1 | svc podman load
PRAXIS_IMAGE=$(svc podman image inspect "$PRAXIS_IMAGE" --format '{{.Id}}')
MAAS_IMAGE=$(svc podman image inspect localhost/pricetag-maas:standalone --format '{{.Id}}')
METERING_IMAGE=$(svc podman image inspect localhost/pricetag-metering:upstream-557ceb1 --format '{{.Id}}')
Stop if any image ID is not sha256: followed by 64 lowercase hexadecimal
characters. Image build support does not publish or sign images.
4. Configure the real gateway providers¶
Run only on pricetag-real. The helper asks for credentials using hidden
prompts, performs HTTPS model discovery, and stores provider secrets separately
from the MaaS user keys. Choose one or both provider types:
cd ~/secure-single-server
sudo python3 scripts/pricetag/providers openai
sudo python3 scripts/pricetag/providers pricetag
sudo python3 scripts/pricetag/providers export
For PriceTag, enter the Messages hostname once; the helper derives the paired
OpenAI hostname when applicable and adds HTTPS and /v1. Use the explicit
discovery options only when the provider documents a nonstandard catalog or
omits an entitled model. A model listed locally is not proof that the provider
will accept an inference request. Review the generated model IDs, API types,
limits and price mappings before preparing the host.
Skip this section on pricetag-perf; it uses only the synthetic provider.
5. Prepare and start the services¶
On the real host, export the provider configuration. Set GATEWAY_HOST to the
DNS name clients will use, or the VM public IP for the initial IP-bound test.
The TLS certificate includes that name and localhost.
GATEWAY_HOST='gateway.example.invalid'
sudo python3 scripts/pricetag/prepare.py \
--hostname "$GATEWAY_HOST" --praxis-image "$PRAXIS_IMAGE" \
--maas-image "$MAAS_IMAGE" --metering-image "$METERING_IMAGE" \
--providers /root/pricetag-provider-inputs/providers.json \
--models /root/pricetag-provider-inputs/models.json \
--provider-unit /root/pricetag-provider-inputs/provider-secrets.container \
--price-sources /root/pricetag-provider-inputs/prices.json \
--monthly-usd 5
sudo scripts/pricetag/start
On the performance host, use the same GATEWAY_HOST selection and run:
GATEWAY_HOST='PERFORMANCE_VM_PUBLIC_IP_OR_DNS'
sudo python3 scripts/pricetag/prepare.py --mock-provider \
--hostname "$GATEWAY_HOST" --praxis-image "$PRAXIS_IMAGE" \
--maas-image "$MAAS_IMAGE" --metering-image "$METERING_IMAGE" \
--price-sources configs/pricetag/perf-price-sources.json --monthly-usd 5
sudo scripts/pricetag/start
The performance price source must be a reviewed local fixture file mapping
fixture to a model already priced in the pinned metering image. Do not use
real provider secrets or aliases on this VM. prepare.py refuses to overwrite
existing deployment directories. start installs and starts user Quadlets;
PostgreSQL, MaaS, metering, the optional mock, and Praxis start on boot. It
selects unused static addresses for MaaS, Praxis and PriceTag on
pricetag-private. MaaS trusts host loopback and only those three /32
service addresses; unrelated containers on the network are excluded.
For an existing deployment, first make a root-only backup of its configuration,
Quadlet units, and PostgreSQL data. Stop the old services, then move
/etc/praxis-pricetag aside and prepare directly at that path with a new
--admin-directory. Preserve the existing pricetag-data volume and provider
Podman secrets. start detects the existing metering-superuser PostgreSQL
layout, creates the MaaS database, updates the application-role passwords, and
preserves existing metering rows. It also detects an existing
pricetag-private subnet, selects unused addresses for MaaS, Praxis and
PriceTag in it, and trusts only those service addresses and host loopback. A new
network uses the subnet in the Quadlet definition. It refuses to apply the
initial USD policy and model aliases when usage_events is nonempty. After
reviewing the backup, schema, and configured price-source IDs, explicitly
adopt that database with:
This applies the reviewed USD policy and missing model aliases idempotently;
it does not delete usage history. The ordinary start command continues to
refuse first-time bootstrap on a nonempty database.
6. Check services and issue user keys¶
On each host, confirm that services are active and user lingering is enabled:
PRICETAG_UID=$(id -u praxis-svc)
svc() {
(cd /tmp && sudo -u praxis-svc env \
XDG_RUNTIME_DIR="/run/user/$PRICETAG_UID" \
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$PRICETAG_UID/bus" "$@")
}
svc systemctl --user status pricetag-db.service pricetag-maas.service \
pricetag-metering.service pricetag-gateway.service --no-pager
loginctl show-user praxis-svc -p Linger
sudo curl --fail --cacert /etc/praxis-pricetag/ca.pem \
https://localhost:8443/login >/dev/null
On first start, the deployment creates an administrator key and Alice and Bob keys, each with a $5 monthly override. Read only the key you need:
sudo find /root/pricetag-admin -maxdepth 1 -type f -name '*.key' -print
sudo cat /root/pricetag-admin/admin.key
sudo cat /root/pricetag-admin/alice.key
Treat these as passwords. Copy keys through a protected channel and delete
temporary copies after installation. Create or rotate a user's named key with
scripts/pricetag/user; use a new output filename because the tool will not
overwrite a credential file:
sudo python3 scripts/pricetag/user --subject alice --name Alice --rotate \
--monthly-usd 5 --output /root/pricetag-admin/alice-next.key
The old key is revoked only after the replacement is saved. For direct key
operations, scripts/pricetag/maas-key provides create, list, rotate and revoke
through MaaS APIs. Set an explicit maximum key lifetime on the MaaS service;
this profile configures 90 days. PriceTag browser sessions have their own
expiry and are not MaaS API keys.
7. Open the dashboard and connect OpenCode¶
On the workstation, copy the public CA and a user's MaaS key into a private directory. Replace the variables with the values printed by AWS verification:
VM=ec2-user@PUBLIC_IP
SSH_KEY="$HOME/.secure-single-server-tests/ssh/$RUN_PREFIX"
CLIENT_DIR="$HOME/.config/praxis-pricetag/$RUN_PREFIX"
mkdir -m 700 -p "$CLIENT_DIR"
umask 077
ssh -o IdentitiesOnly=yes -o ForwardAgent=no -i "$SSH_KEY" "$VM" \
'sudo cat /etc/praxis-pricetag/ca.pem' > "$CLIENT_DIR/ca.pem"
ssh -o IdentitiesOnly=yes -o ForwardAgent=no -i "$SSH_KEY" "$VM" \
'sudo cat /root/pricetag-admin/alice.key' > "$CLIENT_DIR/alice.key"
chmod 600 "$CLIENT_DIR/ca.pem" "$CLIENT_DIR/alice.key"
Open https://PUBLIC_IP:8443/login in a browser, accept or install the
deployment CA, then sign in with the administrator key. Use a separate browser
profile and the user's key for the personal dashboard. Keep TCP 8443 restricted
to approved client IPs until authentication, accounting, quotas, and dashboard
authorization pass the acceptance checks.
Launch OpenCode for one project and one session:
cd /path/to/your/project
python3 /path/to/secure-single-server/scripts/pricetag/harness opencode \
--url https://PUBLIC_IP:8443 --key-file "$CLIENT_DIR/alice.key" \
--ca-file "$CLIENT_DIR/ca.pem" --model openai/gpt-6-luna
Choose a model alias actually enabled in models.json; it is the initial
model for the session. The helper configures temporary OpenAI-compatible and
Anthropic providers with every model in the gateway catalog and its context
and output limits, so OpenCode's model picker
can switch among them. It does not edit persistent OpenCode configuration.
8. Run performance tests and review storage¶
Run the bounded test only on the mock performance server. See the performance procedure for workload, exact ledger reconciliation, resource sampling, database growth and cleanup. Do not mix real-provider requests or real users into the performance report.
Raw usage-event retention is not configured by this deployment. Measure table, WAL, log and disk growth during a soak, keep encrypted off-host backups, and set an explicit retention policy before long-lived service. Per-user dashboard isolation and recovery from an off-host backup remain acceptance gaps; a healthy systemd status is not evidence that these features are qualified.