Tony Ou
11/24/2025, 9:14 AMBartosz Wasilewski
11/26/2025, 8:31 AMOPAL_OPA_V0_COMPAT=true) as recommended
3. Then disabled compatibility mode (OPAL_OPA_V0_COMPAT=false) to test breaking changes
4. Expected our policies to fail (since we didn't add .v1 to package names)
What actually happened: Everything continues to work perfectly, even with compatibility mode disabled.
Here's our policy structure:
package data.role # No .v1 suffix - should this fail in strict v1 mode?
import rego.v1
import data.utils.v1.functions.jwt as functions_jwt
import data.utils.v1.roles as roles
default allow := false
allow if {
functions_jwt.user_has_role(roles.admin)
input.action == "get-roles"
}
According to the migration guide, without .v1 suffix and with compatibility mode disabled, this should fail. But it doesn't.
My questions:
1. Is import rego.v1 now sufficient for v1 compatibility, making the package .v1 suffix optional?
2. Has something changed in OPAL 0.9.0 that we're not aware of?
3. Are we possibly misunderstanding how compatibility mode works?
We want to ensure we're not missing something important before rolling this out to other environments.
Thanks for any insights!Dai Zhang
11/28/2025, 7:53 AMsystem.rego file like below to limit the url.
# system.rego
allow if {
input.method == "POST"
input.path == ["v1", "data", "token"]
}
however, this would block the data update for that url. we know that the best benefit would be real-time data update in opal. so would there be any problem to trigger using curl --request POST '<https://opa.xxx.xxx/data/config>' . great thanks!Nishanth Mathew Joy
12/08/2025, 10:02 AMNishanth Mathew Joy
12/08/2025, 10:03 AMMario Enrico Ragucci
12/12/2025, 7:47 PMKSM
12/22/2025, 10:33 PMSlackbot
02/13/2026, 12:31 PMSlackbot
02/13/2026, 2:23 PMSlackbot
02/13/2026, 2:25 PMSlackbot
02/13/2026, 2:26 PMSlackbot
02/13/2026, 2:26 PMSlackbot
02/13/2026, 2:50 PMSlackbot
02/15/2026, 9:59 PMSlackbot
02/16/2026, 9:50 AMSlackbot
02/16/2026, 1:17 PMLucía Cabanillas
02/23/2026, 9:47 AMAndrea Di Saverio
03/04/2026, 7:19 AM2026-03-03T20:36:11.372421+0000 | 16 | opal_client.policy_store.opa_client | WARNING | OPA client health: False (policy: False, data: True)
2026-03-03T20:36:11.373165+0000 | 16 | uvicorn.protocols.http.httptools_impl | INFO | 10.100.35.127:51372 - "GET /healthcheck HTTP/1.1" 503
2026-03-03T20:36:11.379258+0000 | 16 | uvicorn.protocols.http.httptools_impl | INFO | 10.100.35.127:51384 - "GET /ready HTTP/1.1" 503
Root cause: OPAL pushes each policy individually via PUT /v1/policies/{path} to the OPA agent. With 500+ policies, each PUT takes ~0.5s+, and this duration appears to grow as more policies are loaded (likely because OPA recompiles/re-evaluates the entire policy set on every single insertion (not sure tho)). This sequential loading leads to a prohibitive startup time.
OPA logs:
{"client_addr":"[::1]:37922","level":"info","msg":"Received request.","req_body":"<policy_content>","req_id":3841,"req_method":"PUT","req_params":{},"req_path":"/v1/policies/main/domain/subdomain/policy1.rego","time":"2026-03-03T20:23:39Z"}
{"client_addr":"[::1]:37922","level":"info","msg":"Sent response.","req_id":3841,"req_method":"PUT","req_path":"/v1/policies/main/domain/subdomain/policy1.rego","resp_body":"{}\n","resp_bytes":3,"resp_duration":631.523340,"resp_status":200,"time":"2026-03-03T20:23:39Z"}
{"client_addr":"[::1]:37934","level":"info","msg":"Received request.","req_body":"<policy_content>","req_id":3842,"req_method":"PUT","req_params":{},"req_path":"/v1/policies/main/domain/subdomain/policy2.rego","time":"2026-03-03T20:23:39Z"}
{"client_addr":"[::1]:37934","level":"info","msg":"Sent response.","req_id":3842,"req_method":"PUT","req_path":"/v1/policies/main/domain/subdomain/policy2.rego","resp_body":"{}\n","resp_bytes":3,"resp_duration":716.441353,"resp_status":200,"time":"2026-03-03T20:23:39Z"}
What I've considered to solve: one idea is to use bundles and pre-bake the policies into the OPA container image (e.g. via opa run --bundle /policies, or different methods), so that OPA boots up instantly with all policies already compiled. However, OPAL client has no concept of a "starting commit hash" or awareness that OPA already has the policies — its internal state is in-memory and lost on every restart. So it would still perform the full sync via individual PUTs on every cold start, making the pre-bake effectively useless unless there's a way to tell OPAL "the store is already up-to-date as of commit X — only sync the delta."
I’d be very interested to know:
- whether others have seen similar behavior at this scale and how you handled/mitigated this
- whether there is a way to make OPAL skip the initial full sync if OPA is already pre-loaded (e.g. by providing the last policy commit hash present in the bundle)
- whether there is a way to switch OPAL to deliver policies as an OPA bundle instead of individual PUT calls
Any real-world experience or recommendations would be very helpful. Thanks!!! 🙏☺️Saliya Samarawickrama
03/12/2026, 9:36 AM2026-03-12T09:20:44.817436+0000 | fastapi_websocket_rpc.websocket_rpc_e...| INFO | Client disconnected - 43490 :: d78ae83fc0b4447a98a544a81f7465e0
2026-03-12T09:20:44.817686+0000 | websockets.legacy.server | INFO | connection closed
2026-03-12T09:20:44.889455+0000 | fastapi_websocket_rpc.websocket_rpc_e...| INFO | Client connected
2026-03-12T09:20:44.889886+0000 | websockets.legacy.server | INFO | connection open
2026-03-12T09:20:44.894689+0000 | fastapi_websocket_rpc.websocket_rpc_e...| INFO | Client disconnected - 39376 :: dee642b8daa4491a8ba991f19a80b981
2026-03-12T09:20:44.894872+0000 | websockets.legacy.server | INFO | connection closed
2026-03-12T09:20:46.382346+0000 | fastapi_websocket_rpc.websocket_rpc_e...| INFO | Client connected
2026-03-12T09:20:46.382868+0000 | websockets.legacy.server | INFO | connection open
2026-03-12T09:20:46.388795+0000 | fastapi_websocket_rpc.websocket_rpc_e...| INFO | Client disconnected - 37782 :: 44457ab56137481e9fb742fd39fe10b0
2026-03-12T09:20:46.389028+0000 | websockets.legacy.server | INFO | connection closed
2026-03-12T09:20:47.124470+0000 | fastapi_websocket_rpc.websocket_rpc_e...| INFO | Client connected
OPAL client log:
File "/usr/local/lib/python3.10/site-packages/fastapi_websocket_rpc/rpc_channel.py", line 433, in async_call
await self.send(msg)
│ │ └ RpcMessage(request=RpcRequest(method='_ping_', arguments={}, call_id='5369bcdcc34a4b4a9c32e3837ddec06a'), response=None)
│ └ <function RpcChannel.send at 0x7c45cc2e69e0>
└ <fastapi_websocket_rpc.rpc_channel.RpcChannel object at 0x7c45c541a890>
File "/usr/local/lib/python3.10/site-packages/fastapi_websocket_rpc/rpc_channel.py", line 211, in send
await self.socket.send(data)
│ │ │ └ RpcMessage(request=RpcRequest(method='_ping_', arguments={}, call_id='5369bcdcc34a4b4a9c32e3837ddec06a'), response=None)
│ │ └ <function JsonSerializingWebSocket.send at 0x7c45cc2e7c70>
│ └ <fastapi_websocket_rpc.simplewebsocket.JsonSerializingWebSocket object at 0x7c45c54183d0>
└ <fastapi_websocket_rpc.rpc_channel.RpcChannel object at 0x7c45c541a890>
File "/usr/local/lib/python3.10/site-packages/fastapi_websocket_rpc/simplewebsocket.py", line 44, in send
await self._websocket.send(self._serialize(msg))
│ │ │ │ │ └ RpcMessage(request=RpcRequest(method='_ping_', arguments={}, call_id='5369bcdcc34a4b4a9c32e3837ddec06a'), response=None)
│ │ │ │ └ <function JsonSerializingWebSocket._serialize at 0x7c45cc2e7b50>
│ │ │ └ <fastapi_websocket_rpc.simplewebsocket.JsonSerializingWebSocket object at 0x7c45c54183d0>
│ │ └ <function WebSocketsClientHandler.send at 0x7c45cc2fc280>
│ └ <fastapi_websocket_rpc.websocket_rpc_client.WebSocketsClientHandler object at 0x7c45c753c280>
└ <fastapi_websocket_rpc.simplewebsocket.JsonSerializingWebSocket object at 0x7c45c54183d0>
File "/usr/local/lib/python3.10/site-packages/fastapi_websocket_rpc/websocket_rpc_client.py", line 147, in send
await self._websocket.send(msg)
│ │ │ └ '{"request": {"method": "_ping_", "arguments": {}, "call_id": "5369bcdcc34a4b4a9c32e3837ddec06a"}, "response": null}'
│ │ └ <function Connection.send at 0x7c45c754cd30>
│ └ <websockets.asyncio.client.ClientConnection object at 0x7c45c547c790>
└ <fastapi_websocket_rpc.websocket_rpc_client.WebSocketsClientHandler object at 0x7c45c753c280>
File "/usr/local/lib/python3.10/site-packages/websockets/asyncio/connection.py", line 476, in send
async with self.send_context():
│ └ <function Connection.send_context at 0x7c45c754d360>
└ <websockets.asyncio.client.ClientConnection object at 0x7c45c547c790>
File "/usr/local/lib/python3.10/contextlib.py", line 199, in __aenter__
return await anext(self.gen)
│ └ <async_generator object Connection.send_context at 0x7c45c75f5fc0>
└ <contextlib._AsyncGeneratorContextManager object at 0x7c45c541aaa0>
File "/usr/local/lib/python3.10/site-packages/websockets/asyncio/connection.py", line 957, in send_context
raise self.protocol.close_exc from original_exc
│ │ │ └ None
│ │ └ <property object at 0x7c45c7e9f830>
│ └ <websockets.client.ClientProtocol object at 0x7c45c547c6d0>
└ <websockets.asyncio.client.ClientConnection object at 0x7c45c547c790>
websockets.exceptions.ConnectionClosedOK: received 1000 (OK); then sent 1000 (OK)
2026-03-12T09:21:48.511830+0000 | opal_client.policy_store.opa_client |WARNING | OPA client health: False (policy: False, data: False)
2026-03-12T09:21:48.512277+0000 | uvicorn.protocols.http.httptools_impl | INFO | 10.246.16.157:55704 - "GET /healthcheck HTTP/1.1" 503
2026-03-12T09:21:52.514744+0000 | opal_client.policy_store.opa_client |WARNING | OPA client health: False (policy: False, data: False)
2026-03-12T09:21:52.515261+0000 | uvicorn.protocols.http.httptools_impl | INFO | 10.246.16.157:57878 - "GET /healthcheck HTTP/1.1" 503
2026-03-12T09:22:04.668932+0000 | opal_client.policy_store.opa_client |WARNING | OPA client health: False (policy: False, data: False)
2026-03-12T09:22:04.669366+0000 | uvicorn.protocols.http.httptools_impl | INFO | 10.246.16.157:54528 - "GET /healthcheck HTTP/1.1" 503
2026-03-12T09:22:04.672225+0000 | opal_client.policy_store.opa_client |WARNING | OPA client health: False (policy: False, data: False)
2026-03-12T09:22:04.672794+0000 | uvicorn.protocols.http.httptools_impl | INFO | 10.246.16.157:54536 - "GET /healthcheck HTTP/1.1" 503
2026-03-12T09:22:19.672953+0000 | opal_client.policy_store.opa_client |WARNING | OPA client health: False (policy: False, data: False)
2026-03-12T09:22:19.673463+0000 | uvicorn.protocols.http.httptools_impl | INFO | 10.246.16.157:44232 - "GET /healthcheck HTTP/1.1" 503
2026-03-12T09:22:34.672830+0000 | opal_client.policy_store.opa_client |WARNING | OPA client health: False (policy: False, data: False)
2026-03-12T09:22:34.673382+0000 | uvicorn.protocols.http.httptools_impl | INFO | 10.246.16.157:35314 - "GET /healthcheck HTTP/1.1" 503Vivek Garg
03/23/2026, 4:00 PMMarcelo Almeida
03/24/2026, 6:04 PMJon Asbury
03/31/2026, 7:46 PMPrasenjit Roy
04/12/2026, 4:44 PME2
05/20/2026, 1:46 AMSlackbot
05/20/2026, 7:30 PMphil.lewis
06/11/2026, 7:48 AMVitaly Orbidan
08/20/2026, 9:57 PMpolicies folder
I am trying to use the .manifest in the root folder to specify which libraries I want to load.
The directory structure is like this:
policies
│ ├── .manifest
│ ├── dir1
│ │ ├── .manifest
│ │ └── authz
│ │ └── policy.rego
│ ├── dir2
│ │ ├── .manifest
│ │ └── authz
│ │ └── policy.rego
│ └── library
│ └── utils
│ ├── .manifest
│ └── policy.rego
The root .manifest contents:
dir1/
dir2/
In each dir the .manifest is the same:
../library/utils/policy.rego
authz/policy.rego
On the server I am getting warnings:
2026-08-20T20:49:16.204656+0000 | opal_common.git_utils.bundle_maker |WARNING | Path 'policies/dir1/../library/utils/policy.rego' is outside current .manifest directory
2026-08-20T20:49:16.213027+0000 | opal_common.git_utils.bundle_maker |WARNING | Path 'policies/dir2/../library/utils/policy.rego' is outside current .manifest directory
Can't I reference rego policies outside of the current tree?Marcus Nyh
09/28/2026, 1:03 PM<date_time> | 17 | opal_common.sources.api_policy_source |ERROR | Failed to load initial policy from remote API bundle server
Traceback (most recent call last):
File "/usr/local/bin/gunicorn", line 33, in <module>
sys.exit(load_entry_point('gunicorn==26.2.0', 'console_scripts', 'gunicorn')())
File "/usr/local/lib/python3.10/site-packages/gunicorn/app/wsgiapp.py", line 66, in run
WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]", prog=prog).run()
File "/usr/local/lib/python3.10/site-packages/gunicorn/app/base.py", line 235, in run
super().run()
File "/usr/local/lib/python3.10/site-packages/gunicorn/app/base.py", line 71, in run
Arbiter(self).run()
File "/usr/local/lib/python3.10/site-packages/gunicorn/arbiter.py", line 248, in run
self.manage_workers()
File "/usr/local/lib/python3.10/site-packages/gunicorn/arbiter.py", line 701, in manage_workers
self.spawn_workers()
File "/usr/local/lib/python3.10/site-packages/gunicorn/arbiter.py", line 788, in spawn_workers
self.spawn_worker()
File "/usr/local/lib/python3.10/site-packages/gunicorn/arbiter.py", line 753, in spawn_worker
worker.init_process()
File "/usr/local/lib/python3.10/site-packages/gunicorn/workers/base.py", line 145, in init_process
self.run()
File "/usr/local/lib/python3.10/site-packages/uvicorn/workers.py", line 104, in run
return asyncio_run(self._serve(), loop_factory=self.config.get_loop_factory())
File "/usr/local/lib/python3.10/site-packages/uvicorn/_compat.py", line 60, in asyncio_run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.10/site-packages/opal_common-0.0.0-py3.10.egg/opal_common/sources/base_policy_source.py", line 54, in run
await self.get_initial_policy_state_from_remote()
> File "/usr/local/lib/python3.10/site-packages/opal_common-0.0.0-py3.10.egg/opal_common/sources/api_policy_source.py", line 88, in get_initial_policy_state_from_remote
self.local_git = self.tar_to_git.create_local_git()
File "/usr/local/lib/python3.10/site-packages/opal_common-0.0.0-py3.10.egg/opal_common/git_utils/tar_file_to_local_git_extractor.py", line 56, in create_local_git
local_git = self.commit_local_git(should_init=True)
File "/usr/local/lib/python3.10/site-packages/opal_common-0.0.0-py3.10.egg/opal_common/git_utils/tar_file_to_local_git_extractor.py", line 46, in commit_local_git
local_git.index.add(self.policy_bundle_git_add_pattern)
File "/usr/local/lib/python3.10/site-packages/git/index/base.py", line 925, in add
paths, entries = self._preprocess_add_items(items)
File "/usr/local/lib/python3.10/site-packages/git/index/base.py", line 710, in _preprocess_add_items
paths.append(self._to_relative_path(item))
File "/usr/local/lib/python3.10/site-packages/git/index/base.py", line 696, in _to_relative_path
return _to_relative_path(cast(PathLike, self.repo.working_tree_dir), path)
File "/usr/local/lib/python3.10/site-packages/git/util.py", line 373, in _to_relative_path
raise ValueError("Path %r is not in repository at %r" % (path_str, root_abs))
ValueError: Path '*' is not in repository at '//opal/opal/opal_repo_clone'3kd1000
11/03/2026, 5:30 AMrelease/0.9.x would be welcome. Thanks!Marcus Nyh
11/04/2026, 8:37 AM