Disclaimer: This is just what has worked for me, I'm nowhere near to being an expert.
From my point of view, the chance of non-deterministic stuff happening if you run your server and client in separate threads "feels" wrong for a singleplayer game. I've approached this problem by splitting each client and server frame in two. The sequence is the same regardless of whether you're in singleplayer, running a dedicated server, connected to a remote server or whatever, just with some steps removed. A simplified overview:
First, the client processes all its input and sends it to the server. Local movement prediction is performed.
Second, the server reads the input it has gathered for the upcoming frame and prepares to step the simulation.
Third, the physics simulation is stepped. This is not controlled by either client or server module, and so always happens exactly once regardless of which modules are running.
Forth, The server integrates the results of the physics step and updates the game state. It then sends the game state to the clients.
Fifth, the client integrates the most-recent state is has received from the server.
I use component-based entities, where some component structures are shared between the client and the server. Most notably, physics and movement state components are shared. This means that if you are running both the client and the server, you don't need to "send" that part of the game state, the client has direct access to it in the fifth step. You also don't need to do local movement prediction since the available state is perfectly up to date anyway. The one complication in this is determining whether the client or the server is responsible for allocating and releasing the shared components. For now, I'm using a very simple approach of setting a value when the game starts and referring to that before trying to execute any "both module" code. There's probably a more elegant solution, but this is sufficient for my needs.
As for prediction, as a general rule you should only be predicting your own motion. In your example of opening a door, if there's a lead-in animation for your character before the door opens, go ahead and start that, but don't actually open the door until the server says so. You're aiming to give immediate feedback to your player inputs, but not make significant changes to the world.